Rust box a single trait that encompasses generic traits - rust

I am in a situation where I want some objects to implement a trait, say "Base", and some other objects will implements a trait "Super". The Super trait also has to be generic for T : Base so that I can automatically implement parts of Super based on which Base it was specialized with. Now this seems to work fine with the following trivial example
trait Base {
fn say_hi() -> &'static str;
}
struct BaseOne {}
struct BaseTwo {}
impl Base for BaseOne {
fn say_hi() -> &'static str {
"hi!"
}
}
impl Base for BaseTwo {
fn say_hi() -> &'static str {
"hello!"
}
}
trait Super<T: Base> {
fn say_hi(&self) -> &'static str {
T::say_hi()
}
}
struct SuperOne;
struct SuperTwo;
impl Super<BaseOne> for SuperOne {}
impl Super<BaseTwo> for SuperTwo {}
My problem comes in with my next requirement, which is that I want to be able to store a vector of objects which implement Super, regardless of which Base it is specialized for. My idea for this is to create a trait that covers all Supers, such as the AnySuper trait below
trait AnySuper {
fn say_hi(&self) -> &'static str;
}
impl<T> AnySuper for dyn Super<T> where T : Base {
fn say_hi(&self) -> &'static str {
Super::say_hi(self)
}
}
And then store a vector of Box such as in the example below
fn main() {
let one = Box::new(SuperOne);
let two = Box::new(SuperTwo);
let my_vec: Vec<Box<dyn AnySuper>> = Vec::new();
my_vec.push(one);
my_vec.push(two);
}
But unfortunately that fails with the following error
error[E0277]: the trait bound `SuperOne: AnySuper` is not satisfied
--> src/main.rs:52:17
|
52 | my_vec.push(one);
| ^^^ the trait `AnySuper` is not implemented for `SuperOne`
|
= note: required for the cast to the object type `dyn AnySuper`
error[E0277]: the trait bound `SuperTwo: AnySuper` is not satisfied
--> src/main.rs:53:17
|
53 | my_vec.push(two);
| ^^^ the trait `AnySuper` is not implemented for `SuperTwo`
|
= note: required for the cast to the object type `dyn AnySuper`
Which is a bit strange because in my mind I have implemented AnySuper for all Super<T>. So my question is, am I doing something fundamentally wrong or is there just an issue with my syntax?
P.S. I have set up a playground with this code at https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a54e3e9044f1edaeb24d8ad934eaf7ec if anyone wants to play around with it.

I think something strange is happening since you're going through two layers of trait object indirection, i.e. from the concrete Box<SuperOne> to Box<dyn Super<BaseOne>> to Box<dyn AnySuper>. Certainly, Rust is equipped to handle the first case, as we do that all the time, but the second is not something I've seen.
From the sound of it, though, you want to say "AnySuper is implemented whenever Super is for any T", and what you've written in the code is "AnySuper is implemented for this one funny trait object type called dyn Super<T>". Let's try to write an actual blanket implementation.
impl<S, T> AnySuper for S where S : Super<T>, T : Base
But now we get some rather exciting error messages about unconstrained types.
error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates
--> src/main.rs:36:9
|
36 | impl<S, T> AnySuper for S where S : Super<T>, T : Base {
| ^ unconstrained type parameter
You can read more about the motivation for this error on the relevant RFC, but the bottom line is that Rust can't figure out what T should be for some arbitrary S.
And that's true in general. If you give Rust some arbitrary thing that implements Super<T>, it might implement Super<T> for several different T, and then AnySuper would have to choose between those, which it can't do. Instead, we need to promise Rust that, for a given implementor, there's only one possible T, and we can do that by making T an associated type.
trait Super {
type T : Base;
fn say_hi(&self) -> &'static str {
Self::T::say_hi()
}
}
impl Super for SuperOne {
type T = BaseOne;
}
impl Super for SuperTwo {
type T = BaseTwo;
}
impl<S> AnySuper for S where S : Super, S::T : Base {
fn say_hi(&self) -> &'static str {
Super::say_hi(self)
}
}
Now Rust will happily accept your vector of AnySuper.
Rust Playground link
Working code example:
trait Base {
fn say_hi() -> &'static str;
}
struct BaseOne {}
struct BaseTwo {}
impl Base for BaseOne {
fn say_hi() -> &'static str {
"hi!"
}
}
impl Base for BaseTwo {
fn say_hi() -> &'static str {
"hello!"
}
}
trait Super {
type T : Base;
fn say_hi(&self) -> &'static str {
Self::T::say_hi()
}
}
struct SuperOne;
struct SuperTwo;
impl Super for SuperOne {
type T = BaseOne;
}
impl Super for SuperTwo {
type T = BaseTwo;
}
trait AnySuper {
fn say_hi(&self) -> &'static str;
}
impl<S> AnySuper for S where S : Super, S::T : Base {
fn say_hi(&self) -> &'static str {
Super::say_hi(self)
}
}
fn main() {
let one = Box::new(SuperOne);
let two = Box::new(SuperTwo);
let mut my_vec: Vec<Box<dyn AnySuper>> = Vec::new();
my_vec.push(one);
my_vec.push(two);
println!("Success!");
}

Related

Can I store an `impl Future` as a concrete type?

tokio::net::TcpStream::connect is an async function, meaning it returns an existential type, impl Future. I would like to store a Vec of these futures in a struct. I've found many questions where someone wants to store multiple different impl Futures in a list, but I only want to store the return type of one. I feel like this should be possible without Box<dyn Future> as I am really only storing a single concrete type, but I cannot figure out how without getting found opaque type errors.
It is possible with the nightly feature min_type_alias_impl_trait. The trick is to create a type alias, and a dummy function from which the compiler can infer a defining use.
#![feature(min_type_alias_impl_trait)]
use tokio::net::TcpStream;
use core::future::Future;
type TcpStreamConnectFut = impl Future<Output = std::io::Result<TcpStream>>;
fn __tcp_stream_connect_defining_use() -> TcpStreamConnectFut {
TcpStream::connect("127.0.0.1:8080")
}
struct Foo {
connection_futs: Vec<TcpStreamConnectFut>,
}
This compiles, but does not work as expected:
impl Foo {
fn push(&mut self) {
self.connection_futs.push(TcpStream::connect("127.0.0.1:8080"));
}
}
error[E0308]: mismatched types
--> src/lib.rs:18:35
|
6 | type TcpStreamConnectFut = impl Future<Output = std::io::Result<TcpStream>>;
| ------------------------------------------------ the expected opaque type
...
18 | self.connection_futs.push(TcpStream::connect("127.0.0.1:8080"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected opaque type, found a different opaque type
|
= note: while checking the return type of the `async fn`
= note: expected opaque type `impl Future` (opaque type at <src/lib.rs:6:28>)
found opaque type `impl Future` (opaque type at </playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.7.1/src/net/tcp/stream.rs:111:56>)
= help: consider `await`ing on both `Future`s
= note: distinct uses of `impl Trait` result in different opaque types
Using the dummy function we created does work:
impl Foo {
fn push(&mut self) {
self.connection_futs.push(__tcp_stream_connect_defining_use());
}
}
So we can just create wrapper functions:
fn tcp_stream_connect<A: ToSocketAddrs>(addr: A) -> TcpStreamConnectFut {
TcpStream::connect(addr)
}
Except...
error: type parameter `A` is part of concrete type but not used in parameter list for the `impl Trait` type alias
--> src/main.rs:9:74
|
9 | fn tcp_stream_connect<A: ToSocketAddrs>(addr: A) -> TcpStreamConnectFut {
| __________________________________________________________________________^
10 | | TcpStream::connect(addr)
11 | | }
| |_^
We could just use String or &'static str, and the entire thing compiles:
type TcpStreamConnectFut = impl Future<Output = std::io::Result<TcpStream>>;
fn tcp_stream_connect(addr: &'static str) -> TcpStreamConnectFut {
TcpStream::connect(addr)
}
struct Foo {
connection_futs: Vec<TcpStreamConnectFut>,
}
impl Foo {
fn push(&mut self) {
self.connection_futs.push(tcp_stream_connect("..."));
}
}
You can also add a generic parameter to the type alias itself, but that probably doesn't make sense in this case:
type TcpStreamConnectFut<A> = impl Future<Output = std::io::Result<TcpStream>>;
fn tcp_stream_connect<A: ToSocketAddrs>(addr: A) -> TcpStreamConnectFut<A> {
TcpStream::connect(addr)
}
struct Foo {
connection_futs: Vec<TcpStreamConnectFut<&'static str>>,
}
impl Foo {
fn push(&mut self) {
self.connection_futs.push(tcp_stream_connect("..."));
}
}
So it is possible, but there are a couple of restrictions. I'm not sure how many of these are bugs, and how much of it is intentional behavior. There has been discussion about a typeof operator to make this easier, but this is what we've got at the moment.
You can just use a good old Vec, just like that:
use core::future::Future;
use tokio::net::TcpStream;
fn just_vec() -> Vec<impl Future<Output = std::io::Result<TcpStream>>> {
let mut v = Vec::new();
// connect to several streams
v.push(TcpStream::connect("127.0.0.1:8080"));
v.push(TcpStream::connect("127.0.0.2:8080"));
v
}
However, it gets more tricky if you want to store it in a struct, because unlike above, where concrete type can be inferred, you need to be more explicit with structs.
One stable way to do this, is to use a generic struct. This actually very similar to storing a closure (where you don't have a concrete type either).
use core::future::Future;
use tokio::net::TcpStream;
use tokio::io::AsyncWriteExt;
struct Foo<T> {
connections: Vec<T>,
}
/// This is just like any other vec-wrapper
impl<T> Foo<T> {
pub fn new() -> Self {
Self {
connections: Vec::new(),
}
}
pub fn push(&mut self, conn: T) {
self.connections.push(conn);
}
}
/// Some more specific functios that actually need the Future
impl<T> Foo<T> where T: Future<Output = std::io::Result<TcpStream>> {
pub async fn broadcast(self, data: &[u8]) -> std::io::Result<()> {
for stream in self.connections {
stream.await?.write_all(data).await?
}
Ok(())
}
}
async fn with_struct() -> std::io::Result<()> {
let mut foo = Foo::new();
// connect to several streams
foo.push(TcpStream::connect("127.0.0.1:8080"));
foo.push(TcpStream::connect("127.0.0.2:8080"));
// Do something with the connections
foo.broadcast(&[1,2,3]).await
}

Return of a trait that is implemented by two structs based on predefined string format [duplicate]

I'm trying to create a function that returns an instance of the Shader trait. Here is my drastically simplified code:
trait Shader {}
struct MyShader;
impl Shader for MyShader {}
struct GraphicsContext;
impl GraphicsContext {
fn create_shader(&self) -> Shader {
let shader = MyShader;
shader
}
}
fn main() {}
However I receive the following error:
error[E0277]: the trait bound `Shader + 'static: std::marker::Sized` is not satisfied
--> src/main.rs:10:32
|
10 | fn create_shader(&self) -> Shader {
| ^^^^^^ `Shader + 'static` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `Shader + 'static`
= note: the return type of a function must have a statically known size
Newer versions of the compiler have this error:
error[E0277]: the size for values of type `(dyn Shader + 'static)` cannot be known at compilation time
--> src/main.rs:9:32
|
9 | fn create_shader(&self) -> Shader {
| ^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `(dyn Shader + 'static)`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: the return type of a function must have a statically known size
This makes sense as the compiler doesn't know the size of the trait, but nowhere can I find the recommended way of fixing this.
Passing back a reference with & wouldn't work as far as I know because the reference would outlive the lifetime of its creator.
Perhaps I need to use Box<T>?
Rust 1.26 and up
impl Trait now exists:
fn create_shader(&self) -> impl Shader {
let shader = MyShader;
shader
}
It does have limitations, such as not being able to be used in a trait method and it cannot be used when the concrete return type is conditional. In those cases, you need to use the trait object answer below.
Rust 1.0 and up
You need to return a trait object of some kind, such as &T or Box<T>, and you're right that &T is impossible in this instance:
fn create_shader(&self) -> Box<Shader> {
let shader = MyShader;
Box::new(shader)
}
See also:
What is the correct way to return an Iterator (or any other trait)?
Conditionally iterate over one of several possible iterators
I think this is what you were searching for; a simple factory implemented in Rust:
pub trait Command {
fn execute(&self) -> String;
}
struct AddCmd;
struct DeleteCmd;
impl Command for AddCmd {
fn execute(&self) -> String {
"It add".into()
}
}
impl Command for DeleteCmd {
fn execute(&self) -> String {
"It delete".into()
}
}
fn command(s: &str) -> Option<Box<Command + 'static>> {
match s {
"add" => Some(Box::new(AddCmd)),
"delete" => Some(Box::new(DeleteCmd)),
_ => None,
}
}
fn main() {
let a = command("add").unwrap();
let d = command("delete").unwrap();
println!("{}", a.execute());
println!("{}", d.execute());
}
I think you can use generics and static dispatch (I have no idea if those are the right terms, I just saw someone else use them) to create something like this.
This isn't exactly "returning as a trait", but it is letting functions use traits generically. The syntax is a little obscure, in my opinion, so it's easy to miss.
I asked Using generic iterators instead of specific list types about returning the Iterator trait. It gets ugly.
In the playground:
struct MyThing {
name: String,
}
trait MyTrait {
fn get_name(&self) -> String;
}
impl MyTrait for MyThing {
fn get_name(&self) -> String {
self.name.clone()
}
}
fn as_trait<T: MyTrait>(t: T) -> T {
t
}
fn main() {
let t = MyThing {
name: "James".to_string(),
};
let new_t = as_trait(t);
println!("Hello, world! {}", new_t.get_name());
}
return Box<shader>. As the size of the type must be fixed so you've to bound the object using box smart pointer.

Why must the associated type be specified in a collection of trait object references?

Here is an offending example (Playground):
// Some traits
trait Behaviour {
type Sub: SubBehaviour;
}
trait SubBehaviour {}
// Some implementations of these traits
struct A;
impl Behaviour for A {
type Sub = B;
}
struct B;
impl SubBehaviour for B {}
// Struct that holds a collection of these traits.
struct Example<'a> {
behaviours: Vec<&'a dyn Behaviour>,
}
impl<'a> Example<'a> {
fn add_behaviour<T: Behaviour>(&mut self, b: &'a T) {
self.behaviours.push(b);
}
}
fn main() {
let b = A;
let mut e = Example {
behaviours: Vec::new(),
};
e.add_behaviour(&b);
}
I get:
error[E0191]: the value of the associated type `Sub` (from trait `Behaviour`) must be specified
--> src/main.rs:17:29
|
3 | type Sub: SubBehaviour;
| ----------------------- `Sub` defined here
...
17 | behaviours: Vec<&'a dyn Behaviour>,
| ^^^^^^^^^ help: specify the associated type: `Behaviour<Sub = Type>`
Why must this type must be specified, particularly in this case where we are only storing a reference to the object? How can I get this code to work?
All types must be statically known at compile time. If Rust would allow different associated types for elements of a Vec, type information could depend on indices which are only known at runtime.
I find it helpful to consider a smaller example:
trait Behaviour {
type T;
fn make_t(&self) -> T;
}
fn foo(my_vec: Vec<&dyn Behaviour>, index: usize) {
let t = my_vec[index].make_t(); //Type of t depends on index
}
You were on the right track to fixing this though. I assume you introduced the SubBehaviour trait because you realized you need to put restrictions of what T can be. The thing is, in that case you don't need an associated type anymore.
trait SubBehaviour {}
trait Behaviour {
fn make_t(&self) -> Box<dyn SubBehaviour>;
fn ref_t(&self) -> &dyn SubBehaviour; // also fine
}
fn some_function(my_vec: Vec<&dyn Behaviour>, index: usize) {
let t1 = my_vec[index].make_t();
}
The only limitation is that in your definition of Behaviour you can not do anything which would depend on the size of T, (like allocating it on the stack or moving it) since the size of T can not be specified by the SubBehaviour trait.
You need to specify the associated type of the trait (i.e. Behavior<Sub = ???>).
When adding the associated type at all places, it compiles:
struct Example<'a, S: SubBehaviour + 'a> {
behaviours: Vec<&'a Behaviour<Sub = S>>,
}
impl<'a, S: SubBehaviour> Example<'a, S> {
fn add_behaviour<T: Behaviour<Sub = S>>(&mut self, b: &'a T) {
self.behaviours.push(b);
}
}
See this in action on the Playground
So the answer to your first question is covered by Tim's answer and is correct, you might not want your Example to be generic. In that case, you need to use some sort of type erasure:
// Some traits
trait Behaviour {
type Sub: SubBehaviour;
}
trait SubBehaviour {}
// Some implementations of these traits
struct A;
impl Behaviour for A {
type Sub = B;
}
struct B;
impl SubBehaviour for B {}
struct AnyBehaviour {
closure: Box<Fn()>,
}
impl AnyBehaviour {
fn new<U: SubBehaviour, T: Behaviour<Sub = U>>(b: &T) -> Self {
let closure = || {
//let sub = T::Sub::new();
println!("Can use T here");
};
AnyBehaviour {
closure: Box::new(closure),
}
}
}
// Struct that holds a collection of these traits.
struct Example {
behaviours: Vec<AnyBehaviour>,
}
impl Example {
fn add_behaviour<U: SubBehaviour, T: Behaviour<Sub = U>>(&mut self, b: &T) {
self.behaviours.push(AnyBehaviour::new(b));
}
}
fn main() {
let b = A;
let mut e = Example {
behaviours: Vec::new(),
};
e.add_behaviour(&b);
}
Within the closure, you have access to all the types needed call the traits functions with whatever subtype needed.
Why this happens, is mostly because you actually need a definition of the associated type in order for the trait to be "complete" so the compiler can work with it. Tim's answer answers that by the definition to be higher up in the chain (outside of Example) instead of inside.

Provided method casting &self to trait object

I have a trait in which I want to provide a method. The method is to be implemented in terms of some helpers that have no business being inside the trait and are non-trivial enough that dynamic polymorphism makes more sense than making them generic. So I have code along the lines of
fn use_trait(x: &Trait) {
println!("object says {}", x.needed());
}
trait Trait {
fn needed(&self) -> &str;
fn provided(&self) {
use_trait(self);
}
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
}
fn main() {
Struct().provided();
}
Which, however, does not compile, with error:
error[E0277]: the trait bound `Self: std::marker::Sized` is not satisfied
--> <anon>:9:19
|
9 | use_trait(self);
| ^^^^ the trait `std::marker::Sized` is not implemented for `Self`
|
= help: consider adding a `where Self: std::marker::Sized` bound
= note: required for the cast to the object type `Trait`
I understand why—it is not guaranteed somebody won't implement the trait for an unsized type (converting from &T where T: Trait to &Trait requires T: Sized, but the declaration does not require that).
However, the advice will not do what I need. I can add
fn needed(&self) -> &str where Self: Sized
but then the needed() method won't be accessible on &Trait (because Trait : ?Sized), which renders the thing useless, because the type (the actual one that does something useful) is always handled as Arc<Trait>. And adding
trait Trait: Sized
is even worse, because that does not permit &Trait at all (Trait as a type is unsized, so Trait type does not implement trait Trait).
Of course I can simply make
fn use_trait<T: Trait>(x: &T)
but there is a lot behind it in the real code, so I don't want monomorphisation there especially since the trait is otherwise always handled as trait object.
Is there any way to tell Rust that all types that impl Trait must be sized and here is a definition of a method that should work for all of them?
You need an additional as_trait function on Trait and its implementations:
trait Trait {
fn needed(&self) -> &str;
fn provided(&self) {
use_trait(self.as_trait());
}
fn as_trait(&self) -> &Trait;
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
fn as_trait(&self) -> &Trait {
self as &Trait
}
}
You can try it on the playground. (trait objects)
Enhanced version of #JoshuaEntrekin's answer:
The helper as_trait function can be put in an auxiliary trait that gets blanket implementation for all Sized types trying to implement Trait. Then the implementer of Trait does not have to do anything special and the conversion works.
fn use_trait(x: &Trait) {
println!("object says {}", x.needed());
}
trait Trait : AsTrait {
fn needed(&self) -> &str;
fn provided(&self) where Self : AsTrait {
use_trait(self.as_trait());
}
}
trait AsTrait {
fn as_trait(&self) -> &Trait;
}
impl<T : Trait + Sized> AsTrait for T {
fn as_trait(&self) -> &Trait { self }
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
}
fn main() {
Struct().provided();
}
(on play).
It would also be possible to simply put provided in the auxiliary trait, but then it would have to dynamically dispatch to the other methods of Self unnecessarily.
Update: Actually, the point is that it should still be possible to override provided.
Now the above can be improved further by making it generic. There is std::makrer::Unsize, which is unstable at the time of this writing. We can't make
trait Trait : Unsize<Trait>
because Rust does not allow CRTP, but fortunately it is enough to put the constraint on the method. So
fn use_trait(x: &Trait) {
println!("object says {}", x.needed());
}
trait Trait {
fn needed(&self) -> &str;
fn provided(&self) where Self: AsObj<Trait> {
use_trait(self.as_obj());
}
}
trait AsObj<Tr: ?Sized> {
fn as_obj(&self) -> &Trait;
}
// For &'a Type for Sized Type
impl<Type: Trait> AsObj<Trait> for Type {
fn as_obj(&self) -> &Trait { self }
}
// For trait objects
impl AsObj<Trait> for Trait {
fn as_obj(&self) -> &Trait { self }
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
fn provided(&self) {
println!("Aber dieses Objekt sagt Grüß Gott, Welt!"); // pardon my German, it is rusty.
}
}
fn main() {
let s: &Trait = &Struct();
s.provided();
}
(on play)
This finally makes it transparent for the implementors of other versions.
See also this users thread.

How do I return an instance of a trait from a method?

I'm trying to create a function that returns an instance of the Shader trait. Here is my drastically simplified code:
trait Shader {}
struct MyShader;
impl Shader for MyShader {}
struct GraphicsContext;
impl GraphicsContext {
fn create_shader(&self) -> Shader {
let shader = MyShader;
shader
}
}
fn main() {}
However I receive the following error:
error[E0277]: the trait bound `Shader + 'static: std::marker::Sized` is not satisfied
--> src/main.rs:10:32
|
10 | fn create_shader(&self) -> Shader {
| ^^^^^^ `Shader + 'static` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `Shader + 'static`
= note: the return type of a function must have a statically known size
Newer versions of the compiler have this error:
error[E0277]: the size for values of type `(dyn Shader + 'static)` cannot be known at compilation time
--> src/main.rs:9:32
|
9 | fn create_shader(&self) -> Shader {
| ^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `(dyn Shader + 'static)`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: the return type of a function must have a statically known size
This makes sense as the compiler doesn't know the size of the trait, but nowhere can I find the recommended way of fixing this.
Passing back a reference with & wouldn't work as far as I know because the reference would outlive the lifetime of its creator.
Perhaps I need to use Box<T>?
Rust 1.26 and up
impl Trait now exists:
fn create_shader(&self) -> impl Shader {
let shader = MyShader;
shader
}
It does have limitations, such as not being able to be used in a trait method and it cannot be used when the concrete return type is conditional. In those cases, you need to use the trait object answer below.
Rust 1.0 and up
You need to return a trait object of some kind, such as &T or Box<T>, and you're right that &T is impossible in this instance:
fn create_shader(&self) -> Box<Shader> {
let shader = MyShader;
Box::new(shader)
}
See also:
What is the correct way to return an Iterator (or any other trait)?
Conditionally iterate over one of several possible iterators
I think this is what you were searching for; a simple factory implemented in Rust:
pub trait Command {
fn execute(&self) -> String;
}
struct AddCmd;
struct DeleteCmd;
impl Command for AddCmd {
fn execute(&self) -> String {
"It add".into()
}
}
impl Command for DeleteCmd {
fn execute(&self) -> String {
"It delete".into()
}
}
fn command(s: &str) -> Option<Box<Command + 'static>> {
match s {
"add" => Some(Box::new(AddCmd)),
"delete" => Some(Box::new(DeleteCmd)),
_ => None,
}
}
fn main() {
let a = command("add").unwrap();
let d = command("delete").unwrap();
println!("{}", a.execute());
println!("{}", d.execute());
}
I think you can use generics and static dispatch (I have no idea if those are the right terms, I just saw someone else use them) to create something like this.
This isn't exactly "returning as a trait", but it is letting functions use traits generically. The syntax is a little obscure, in my opinion, so it's easy to miss.
I asked Using generic iterators instead of specific list types about returning the Iterator trait. It gets ugly.
In the playground:
struct MyThing {
name: String,
}
trait MyTrait {
fn get_name(&self) -> String;
}
impl MyTrait for MyThing {
fn get_name(&self) -> String {
self.name.clone()
}
}
fn as_trait<T: MyTrait>(t: T) -> T {
t
}
fn main() {
let t = MyThing {
name: "James".to_string(),
};
let new_t = as_trait(t);
println!("Hello, world! {}", new_t.get_name());
}
return Box<shader>. As the size of the type must be fixed so you've to bound the object using box smart pointer.

Resources