Reputation: 662
Trait objects can be used when we want to store multiple types together in a collection.
But I don't know how to do this when the trait has associated types.
trait Error {}
trait Trait {
type Error: Error;
fn set(&mut self, key: String, value: String) -> Result<(), Self::Error>;
}
struct StructA;
impl Trait for StructA {
type Error = ErrorA;
}
enum ErrorA {}
impl Error for ErrorA {}
struct StructB;
impl Trait for StructB {
type Error = ErrorB;
}
enum ErrorB {}
impl Error for ErrorB {}
fn main() -> Result<(), Box<dyn Error>> {
let value: Box<dyn Trait<Error = dyn Error>> = match 0 {
0 => Box::new(StructA),
_ => Box::new(StructB),
};
value.set(String::from("key"), String::from("value"))?;
Ok(())
}
I have to specify the associated type Box<dyn Trait<Error = _>>
here, but I don't know which type would fit. I tried dyn Error
but it will not work.
Upvotes: 0
Views: 151
Reputation: 154836
If you can change Trait
, you can make it object-safe for all error types by returning a dynamic error type to begin with, e.g. change the signature of set()
to something like:
fn set(&mut self, key: String, value: String) -> Result<(), Box<dyn Error + '_>>;
However, if you can't change Trait
to make it object-safe, you can still create your own object-safe trait, and provide a blanket implementation for every type that implements Trait
. For example:
trait DynamicTrait {
fn set(&mut self, key: String, value: String) -> Result<(), Box<dyn Error + '_>>;
}
impl<T: Trait> DynamicTrait for T {
fn set(&mut self, key: String, value: String) -> Result<(), Box<dyn Error + '_>> {
Trait::set(self, key, value).map_err(|e| Box::new(e) as _)
}
}
DynamicTrait
works exactly like Trait
, but returns Box<dyn Error>
in case of error, so it's object-safe. For example, this just works without modifying the implementation of either Trait
or its implementations StructA
and StructB
:
fn main() {
let value: Box<dyn DynamicTrait> = match 0 {
0 => Box::new(StructA),
_ => Box::new(StructB),
};
}
Upvotes: 2