There's more...

Of all the recipes in this chapter, this one is probably the most stable, as it is being considered for immediate stabilization. The discussion can be found at https://github.com/rust-lang/rust/issues/34511.

While you can use abstract return types for some forms of dependency inversion, you cannot use them for traditional Java-style factories that return different objects depending on a parameter. This is because abstract return types only hide the specific struct returned on the outside of the function, but still internally rely on a specific return value. Because of this, the following code will not compile:

trait Animal {
fn do_sound(&self);
}

struct Dog;
impl Animal for Dog {
fn do_sound(&self) {
println!("Woof");
}
}
struct Cat;
impl Animal for Cat {
fn do_sound(&self) {
println!("Meow");
}
}

enum AnimalType {
Dog,
Cat,
}

fn create_animal(animal_type: AnimalType) -> impl Animal {
match animal_type {
AnimalType::Cat => Cat {},
AnimalType::Dog => Dog {},
}
}

While the outside world doesn't know which animal is going to be returned by create_animal, the function itself needs a specific return type internally. Because the first possible return from our match is an instance of Cat, create_animal assumes that we are going to return no other type. We break that expectation in the next line by returning a Dog, so the compiler fails:

If we want this factory to compile, we need to resort back to Box again:

fn create_animal(animal_type: AnimalType) -> Box<Animal> {
match animal_type {
AnimalType::Cat => Box::new(Cat {}),
AnimalType::Dog => Box::new(Dog {}),
}
}
By the way, this is also exactly what Java does under the hood.

For most purposes, you won't need a factory such as the one presented here. It is considered more idiomatic to use generics with trait bounds.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.82.154