If you want to do something like
struct Foo;
struct Bar;
fn get_foo_bar(want_foo: bool) -> ??? {
if want_foo {
Foo
} else {
Bar
}
}
you have different options, and they are almost the one that have been written in the previous posts:
Enum
struct Foo;
struct Bar;
enum FooBar {
Foo(Foo),
Bar(Bar),
}
fn get_foo_bar(want_foo: bool) -> FooBar {
if want_foo {
FooBar::Foo(Foo)
} else {
FooBar::Bar(Bar)
}
}
This have a very low overhead, and it is what you get whenever you use Option
or Result
. It does not allocate on the heap.
Trait
Here a not working example:
struct Foo;
struct Bar;
trait FooBar {}
impl FooBar for Foo {}
impl FooBar for Bar {}
fn get_foo_bar(want_foo: bool) -> impl FooBar {
if want_foo {
Foo
} else {
Bar
}
}
The reason it is not working is because we are trying to return different concrete types. Even if the two structs are zero-sized, it is not possible to return one or the other (without an Enum
).
What can be done is using virtual dispatching:
struct Foo;
struct Bar;
trait FooBar {}
impl FooBar for Foo {}
impl FooBar for Bar {}
fn get_foo_bar(want_foo: bool) -> Box<dyn FooBar> {
if want_foo {
Box::new(Foo)
} else {
Box::new(Bar)
}
}
In this way you can return any structure that Impl
ements FooBar
, but you have to use the heap to do so.
Any
Sometimes you have only one last resort: Any
use std::any::Any;
struct Foo;
struct Bar;
fn get_foo_bar(want_foo: bool) -> Box<Any> {
if want_foo {
Box::new(Foo)
} else {
Box::new(Bar)
}
}
You can return any possible structure inside Any
, but you must check manually the type of the underlying object, and/or try to downcast to the appropriate type.
So?
When you use Any
or Box<dyn Trait>
(in Rust 2015 you can also write Box<Trait>
, but it is the same) you are using virtual dispatching, otherwise not. No iterator in the standard lib use dynamic dispatching. In the solution posted by @trentj the return value is only quite complex, but is could be written manually (except for the FnMut
type) and it is a concrete, stack allocated type.
You could have written something like
use std::hash::Hash;
fn get_iter<'a, RefIterable, T: 'a>(hs: &'a Option<&'a RefIterable>) -> impl Iterator<Item = &'a T>
where
for<'t> &'t RefIterable: IntoIterator<Item = &'t T>,
T: Eq + Hash,
{
hs.iter().flat_map(|hs| hs.into_iter())
}
In this case you have a generic function, and the output type depends on the input type. However, even in this case in which the concrete type of impl Iterator
is complex as hell, it is deduced at compile time and with zero overhead.
I hope I have been able to help you a bit. 