fn foo(is_number: bool) -> impl Display {
if is_number {
42_u32
} else {
String::from("this is a string")
}
}
fn main() {
foo(true);
}
Syntax wise above code looks like it should work. But it fails to do so . I also understand that this can be solved by Box<dyn triat>. But this made me think what exactly is the usage of impl trait when there is already dyn trait
The restriction of impl Trait that prevents this is that there must still be a single concrete type[1] in place for the impl Trait type. One impl Trait type isn’t the same as another one (e.g. from a different function) even if the trait bound is equal. This is what powers the advantages over Box<dyn Trait> or having a known fixed size, and efficient non-dynamic dispatch: there still exists a concrete type. If you don’t want that, going back to Box<dyn Trait> is usually what you want.
I’ve seen prior discussion about a middle ground: instead of going fully into unsized (or boxed) type dispatched using a vtable, the lighter-weight alternative to unify two types is via an enum, and handwriting that can be slightly boilerplaty. So people have been discussion whether Rust needs a feature along the lines of
// fake syntax to demonstrate the rough idea
fn foo(is_number: bool) -> enum impl Display {
if is_number {
enum { 42_u32 }
} else {
enum { String::from("this is a string") }
}
}
For a specific example, functions that return iterators often hit all three of these categories:
If you use filter or map in the internal implementation, the resulting type contains an (unnameable) closure
Every iterator adapter you call wraps its argument type, so you can easily end up constructing something with a name like Zip<vec::IntoIter<T>, Enumerate<Take<btree_map::Values<'a,K,U>>>>
While iterator adapter chains are quick and easy to implement, they aren't always the most efficient choice. Return types like this, however, restate the implementation and lock you out of implementing a more efficient implementation in the future (w/o a breaking change).
If you instead specify the type above as impl Iterator<Item=(T, (usize, &'a U))>, it's much clearer to the caller what to expect and you leave yourself free to replace the implementation with something better.