Combining a Future with and_then throws compilation error in Rust - rust

I am trying to be comfortable with the idioms of handing Future in Rust.
The idea is that there are two async functions: one's output is another's input.
The first function pairs a bunch of integers along with a bunch of characters.
async fn squeeze_in(elems: [i32;5]) -> Data1 {
let allowed_chars = ['a','b','c','d','e'];
let together = zip(allowed_chars,elems);
let mut data1 = Data1::new();
for _i in together {
data1.append(_i.0,_i.1);
}
data1
}
The other one, takes this list of pairs and gives the the same list but the positions of the elements in a pair, reversed .
async fn blow_out(data1: &Data1) -> Data1Reversed {
let holder: Vec<(i32,char)> = vec![];
let reversed = data1.val1.iter()
.fold(
holder,
| mut accu, next_pair | {
accu.push((next_pair.1,next_pair.0));
accu
}
);
Data1Reversed::new(reversed)
}
The structs Data1 and Data1Reversed are defined, thus:
use std::iter::zip;
use futures::{StreamExt, TryFutureExt};
use futures::FutureExt;
#[derive(Debug)]
struct Data1 {
pub val1: Vec<(char,i32)>
}
impl Data1 {
pub fn new() -> Data1 {
Data1 { val1: vec![] }
}
pub fn append(&mut self, elem_char: char, elem_int: i32) {
self.val1.push((elem_char,elem_int));
}
}
#[derive(Debug)]
struct Data1Reversed {
pub val2: Vec<(i32,char)>
}
impl Data1Reversed {
pub fn new(elems: Vec<(i32,char)>) -> Data1Reversed {
Data1Reversed {
val2: elems
}
}
}
This arrangement, where two functions are called one after the other, after awaiting, works.
However, I want to arrange the flow in a continuation, thus:
let squeezed_and_blown = squeeze_in(some_num).and_then(move |data1| blow_out(&data1)) ;
My understanding is that, each of the functions being async, each produces a future. A flow like a future1.and_then(future2) should be permissible. The compiler doesn't think so, though:
error[E0599]: the method `and_then` exists for opaque type `impl futures::Future<Output = Data1>`, but its trait bounds were not satisfied
--> src/main.rs:38:51
|
38 | let squeezed_and_blown = squeeze_in(some_num).and_then(|data1| blow_out(&data1) ) ;
| ^^^^^^^^ method cannot be called on `impl futures::Future<Output = Data1>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`impl futures::Future<Output = Data1>: TryFuture`
which is required by `impl futures::Future<Output = Data1>: TryFutureExt`
`&impl futures::Future<Output = Data1>: TryFuture`
which is required by `&impl futures::Future<Output = Data1>: TryFutureExt`
`&mut impl futures::Future<Output = Data1>: TryFuture`
which is required by `&mut impl futures::Future<Output = Data1>: TryFutureExt`
I have visited this conversation, if I get an hint of where I am going wrong, but that is not the case. The problem in my case, is with the first future and not what is provided to and_then.
What is confusing me is this message from the compiler:
error[E0599]: the method `and_then` exists for opaque type `impl futures::Future<Output = Data1>`, but its trait bounds were not satisfied
What is it unhappy about?
In response to #Jmb's comment below, here is how I want to use the functions:
// Here, a single future is used. However, the actual functionality requires a set of
// futures to be launched.
let maybe_reveresed =
tokio::spawn({
squeeze_in(some_num)
.and_then(|data1| { blow_out(&data1) })
// and, I can add many such operations, serially here.
}
);
// Here again, the actual functionality requires us to join a number of futures obtained earlier.
let have_we_got_it_reversed = maybe_reveresed.await;
// .. rest of implementation.

Related

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.

Traits returning a trait: In some cases works, in others it doesn't

I need to implement a trait that is returning the futures::StreamExt trait.
In general this sounds easy and there are several answers to this e.g. this here.
I tried this with StreamExt but this does - for some reason - not work. Here my sample code:
// [dependencies]
// futures = "0.3.6"
// async-std = { version = "1.6.5", features = ["attributes", "unstable"] } // interval function is unstable atm.
use std::time::Duration;
use futures::StreamExt;
trait StreamProvidingTrait {
fn returnastream(self: &Self) -> Box<dyn StreamExt<Item=i32>>;
}
struct StreamProvider {}
impl StreamProvidingTrait for StreamProvider {
fn returnastream(self: &Self) -> Box<dyn StreamExt<Item=i32>> {
return Box::new(async_std::stream::interval(Duration::from_millis(1000)).map(|_| 1));
}
}
#[async_std::main]
async fn main() {
let mut counter = 0;
let object = StreamProvider {};
// creates a stream
let worx = object.returnastream();
// mappes the stream into something....
let mut mapped_stream = worx.map(|_| {
counter = counter + 1;
counter
});
// subscribing to the items
while let item = mapped_stream.next().await {
match item {
Some(value) => println!("{}", value),
_ => {}
}
}
}
And here the error:
Compiling traittest v0.1.0 (/Users/andre/repos/traittest)
warning: the trait `futures_util::stream::stream::StreamExt` cannot be made into an object
--> /Users/andre/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.6/src/stream/stream/mod.rs:1326:8
|
1326 | fn poll_next_unpin(&mut self, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>
| ^^^^^^^^^^^^^^^ the trait cannot be made into an object because method `poll_next_unpin` references the `Self` type in its `where` clause
|
= note: `#[warn(where_clauses_object_safety)]` on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #51443 <https://github.com/rust-lang/rust/issues/51443>
error[E0038]: the trait `futures_util::stream::stream::StreamExt` cannot be made into an object
--> src/main.rs:6:36
|
6 | fn returnastream(self: &Self) -> Box<dyn StreamExt<Item=i32>>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `futures_util::stream::stream::StreamExt` cannot be made into an object
|
::: /Users/andre/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.6/src/stream/stream/mod.rs:226:8
|
226 | fn next(&mut self) -> Next<'_, Self>
| ---- the trait cannot be made into an object because method `next` references the `Self` type in its return type
...
976 | fn by_ref(&mut self) -> &mut Self {
| ------ the trait cannot be made into an object because method `by_ref` references the `Self` type in its return type
...
1381 | fn select_next_some(&mut self) -> SelectNextSome<'_, Self>
| ---------------- the trait cannot be made into an object because method `select_next_some` references the `Self` type in its return type
error[E0038]: the trait `futures_util::stream::stream::StreamExt` cannot be made into an object
--> src/main.rs:12:36
|
12 | fn returnastream(self: &Self) -> Box<dyn StreamExt<Item=i32>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `futures_util::stream::stream::StreamExt` cannot be made into an object
|
::: /Users/andre/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.6/src/stream/stream/mod.rs:226:8
|
226 | fn next(&mut self) -> Next<'_, Self>
| ---- the trait cannot be made into an object because method `next` references the `Self` type in its return type
...
976 | fn by_ref(&mut self) -> &mut Self {
| ------ the trait cannot be made into an object because method `by_ref` references the `Self` type in its return type
...
1381 | fn select_next_some(&mut self) -> SelectNextSome<'_, Self>
| ---------------- the trait cannot be made into an object because method `select_next_some` references the `Self` type in its return type
error: aborting due to 2 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0038`.
error: could not compile `traittest`.
To learn more, run the command again with --verbose.
Process finished with exit code 101
When I exchange the StreamExt trait with my own one, this code works perfectly.
trait SomeTrait {
fn returnatrait(self: &Self) -> Box<dyn SomeOtherTrait>;
}
trait SomeOtherTrait {
fn sayHelloWorld(&self);
}
struct DummyStruct {}
impl SomeOtherTrait for DummyStruct {
fn sayHelloWorld(&self) {
println!("hello world");
}
}
struct Implementation {}
impl SomeTrait for Implementation {
fn returnatrait(self: &Self) -> Box<dyn SomeOtherTrait> {
return Box::new(DummyStruct{});
}
}
fn main() {
let implementation = Implementation{};
let worx = implementation.returnatrait();
worx.sayHelloWorld();
}
What's wrong here? There's obviously something I don't understand. Please help me understand this!
A function returning a trait can use the impl Trait syntax to return an opaque type that implements the trait. A trait method returning a trait currently doesn't support this feature and requires the trait to be returned as a trait object - a dynamically dispatched reference or smart pointer such as Box or Rc. Not all traits are object-safe, though, and the bad news is that StreamExt is among those that aren't, for reasons pointed out by the compiler, such as referencing Self in method return types and where clauses.
The good news, however, is that this is not a problem: StreamExt is an extension trait, one that provides a blanket implementation for all types that implement Stream. So you don't need to bother returning a dyn StreamExt trait object, you can return a dyn Stream one, and you'll still get access to StreamExt methods simply by requesting them with use StreamExt. In other words, you can just replace Box<dyn StreamExt> with Box<dyn Stream> in the return type of your trait.
Another issue you might encounter is that Box<dyn Stream> doesn't work on methods that need to move the stream, which includes many methods provided by StreamExt. Those will require the stream to be pinned, which can be fixed by returning Pin<Box<dyn Stream>> instead of Box<dyn Stream>. There is even a boxed() method on StreamExt that pins and boxes the stream in one operation; code that uses it would look like this (playground):
use futures::stream::{Stream, StreamExt};
use std::pin::Pin;
trait StreamProvidingTrait {
fn returnastream(&self) -> Pin<Box<dyn Stream<Item = i32>>>;
}
struct StreamProvider {}
impl StreamProvidingTrait for StreamProvider {
fn returnastream(&self) -> Pin<Box<dyn Stream<Item = i32>>> {
return tokio::stream::once(0).boxed();
}
}
fn main() {
let provider = StreamProvider {};
let stream = provider.returnastream();
let _fut = stream.into_future();
}

How to return an iterator over the keys of a HashMap from a trait implementation?

I'm trying to build a simple graph library in Rust. There is a trait Graph that any graph must implement. This trait has only one function at the moment, nodes, which allows iteration of the graph's nodes using a for-in loop.
An implementation of Graph, MapGraph, is a lightweight wrapper around a HashMap. MapGraph must implement the Graph trait method nodes. I'm having problems getting this to work.
Here's the code for Graph:
pub trait Graph<N> {
fn nodes(&self) -> Box<dyn Iterator<Item = &N>>;
}
And here's the code for MapGraph:
use std::collections::HashMap;
use crate::rep::Graph;
pub struct MapGraph<N> {
map: HashMap<N, HashMap<N, ()>>
}
impl<N> MapGraph<N> {
pub fn new(map: HashMap<N, HashMap<N, ()>>) -> Self {
MapGraph { map }
}
}
impl<N> Graph<N> for MapGraph<N> {
fn nodes(&self) -> Box<dyn Iterator<Item=&N>> {
let keys = self.map.keys();
Box::new(keys)
}
}
The compiler gives this error:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/lib.rs:19:29
|
19 | let keys = self.map.keys();
| ^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 18:5...
--> src/lib.rs:18:5
|
18 | / fn nodes(&self) -> Box<dyn Iterator<Item = &N>> {
19 | | let keys = self.map.keys();
20 | |
21 | | Box::new(keys)
22 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/lib.rs:19:20
|
19 | let keys = self.map.keys();
| ^^^^^^^^
= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the expression is assignable:
expected std::boxed::Box<(dyn std::iter::Iterator<Item = &N> + 'static)>
found std::boxed::Box<dyn std::iter::Iterator<Item = &N>>
I've found other references to this error, but those cases don't seem to look like the one I have here.
I'm using Box because the Graph trait has a function that itself returns a trait. What is the correct way to return an Iterator (or any other trait)? gives this approach as one option, and I haven't been able to implement any of the the others. If there's another way to do this, that would be fine.
What are my options for resolving this specific problem?
It works if you explicitly specify that the trait object (dyn Iterator) that you are returning contains references that are tied to the lifetime of self.
Without adding this bound, the compiler cannot infer from the function signature that the iterator cannot be used after self is moved or destroyed. Because the compiler cannot infer this, it cannot safely use self.map.keys() in the function's output.
Working example with this bound added:
pub trait Graph<N> {
fn nodes<'a>(&'a self) -> Box<dyn Iterator<Item = &N> + 'a>;
}
use std::collections::HashMap;
pub struct MapGraph<N> {
map: HashMap<N, HashMap<N, ()>>,
}
impl<N> MapGraph<N> {
pub fn new(map: HashMap<N, HashMap<N, ()>>) -> Self {
MapGraph { map }
}
}
impl<N> Graph<N> for MapGraph<N> {
fn nodes<'a>(&'a self) -> Box<dyn Iterator<Item = &N> + 'a> {
let keys = self.map.keys();
Box::new(keys)
}
}
Playground
I had thought that a bound of Item = &'a N would also be required, but I guess that's already covered by the "+ 'a"...
N.B. that to make sense of an error like:
expected std::boxed::Box<(dyn std::iter::Iterator<Item = &N> + 'static)>
found std::boxed::Box<dyn std::iter::Iterator<Item = &N>>
you have to understand that the compiler, for ergonomic reasons, automatically adds a + 'static lifetime qualifier to any unqualified trait object. This means that an unqualified Box<dyn MyTrait> is transformed by the compiler into a Box<(dyn MyTrait + 'static)>, which in turn means that the object cannot contain any references except those that last for the lifetime of the entire program.
With this in mind you can see why self.map.keys() does not fit this strict bound, and a more specific explicit bound is required.

Pass a Struct<'a>::method as a `for<'a> Fn(&Foo<'a>)` for use in a closure

In my tests I had a helper function that runs a given method on differently configured objects, with a simplified version looking like this:
fn run_method<F>(f: F)
where
F: Fn(&Foo),
{
let to_test = vec![0i32];
to_test
.iter()
.map(|param| {
let foo = Foo(*param);
f(&foo);
})
.for_each(drop);
}
// run_method(Foo::run);
This worked fine until I added references to the tested struct, making it "lifetime-annotated" (for lack of a proper term, I mean Foo<'a>).
Now I get an error indicating, I think, that Rust doesn't want to accept a Foo::method as a function that can be used with any given lifetime (i.e. F: for<'a> Fn(&Foo<'a>)), as required by the closure:
error[E0631]: type mismatch in function arguments
--> src/main.rs:54:5
|
3 | fn run(&self) {
| ------------- found signature of `for<'r> fn(&'r Foo<'_>) -> _`
...
54 | run_method(Foo::run);
| ^^^^^^^^^^ expected signature of `for<'r, 's> fn(&'r Foo<'s>) -> _`
|
note: required by `run_method`
--> src/main.rs:44:1
|
44 | fn run_method<F>(f: F) where F: Fn(&Foo) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0271]: type mismatch resolving `for<'r, 's> <for<'t0> fn(&'t0 Foo<'_>) {Foo::<'_>::run} as std::ops::FnOnce<(&'r Foo<'s>,)>>::Output == ()`
--> src/main.rs:54:5
|
54 | run_method(Foo::run);
| ^^^^^^^^^^ expected bound lifetime parameter, found concrete lifetime
|
note: required by `run_method`
--> src/main.rs:44:1
|
44 | fn run_method<F>(f: F) where F: Fn(&Foo) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I can work around the problem by avoiding closures (though I don't really understand how 'a gets constrained to be local to run_method - isn't the lifetime parameter supposed to be chosen by the caller?):
fn run_method<'a, F>(f: F)
where
F: Fn(&Foo<'a>),
{
let to_test = vec![&0i32];
for param in to_test {
let foo = Foo(param);
f(&foo);
}
}
Can I fix this without rewriting? If not - is there a reason why this shouldn't work?
Complete code:
struct Foo<'a>(&'a i32);
impl<'a> Foo<'a> {
fn run(&self) {
println!("Hello {}", self.0);
}
}
fn run_method<F>(f: F)
where
F: Fn(&Foo),
{
//same as F: for<'a> Fn(&Foo<'a>) {
let to_test = vec![0i32];
to_test
.iter()
.map(|param| {
let foo = Foo(param);
f(&foo);
})
.for_each(drop);
}
fn main() {
run_method(Foo::run);
}
// This works:
// fn run_method<'a, F>(f: F)
// where
// F: Fn(&Foo<'a>),
// {
// let to_test = vec![&0i32];
// for param in to_test {
// let foo = Foo(param);
// f(&foo);
// }
// }
// The lifetime-less version:
// struct Foo(i32);
// impl Foo {
// fn run(&self) {
// println!("Hello {}", self.0);
// }
// }
//
// fn run_parser_method<F>(f: F)
// where
// F: Fn(&Foo),
// {
// let to_test = vec![0i32];
// to_test
// .iter()
// .map(|param| {
// let foo = Foo(*param);
// f(&foo);
// })
// .for_each(drop);
// }
//
// fn main() {
// run_parser_method(Foo::run);
// }
playground
An overview of other questions about the same error code:
Expected bound lifetime parameter, found concrete lifetime is about mismatch between trait definition and implementation (trait { fn handle<'a>(); } vs impl<'a> { fn handle() {} })
Function references: expected bound lifetime parameter , found concrete lifetime [E0271] as well as Expected bound lifetime parameter, found concrete lifetime [E0271] is about a closure |args| {...} without explicit type annotations (|args: &[&str]|) not being accepted as a Fn(&[&str]) -> (); the answers don't explain why (the latter hints that it was not implemented in 2015)
Type mismatch "bound lifetime parameter" vs "concrete lifetime" when filling a collection from a closure is again about a closure without explicit type annotations specifying that it accepts a reference (let mut insert = |k| seq.insert(k); (1..10).cycle().take_while(insert)), which masks a more useful "borrowed data cannot be stored outside of its closure" error.
I can work around the problem by avoiding closures (though I don't really understand how 'a gets constrained to be local to run_method - isn't the lifetime parameter supposed to be chosen by the caller?)
It is. But when you rewrite it without closures, you have also put the reference inside the vec! invocation, so it is no longer constructed at runtime. Instead, the compiler can infer that to_test has type Vec<&'static i32>, and as 'static outlives any caller-chosen lifetime, there's no violation.
This fails to compile as you expect:
let to_test = vec![0i32];
for param in to_test.iter() {
let foo = Foo(param);
f(&foo);
}
is there a reason why this shouldn't work?
Yes, because the type of run is constrained by the type of Foo. More specifically, you have a lifetime decided by the caller (implicitly, in the type for Foo), so you have to construct a Foo of that lifetime to invoke the given run reference on it.
Can I fix this without rewriting?
That depends.
If all your test values are literals, you can make 'static references.
If you're able and willing to rewrite run, it's possible to make it unconstrained by the type of Foo
impl<'a> Foo<'a> {
fn run<'s>(_self: &Foo<'s>) {
println!("Hello {}", _self.0);
}
}
But then it doesn't need to go in an impl block at all.
I think the solution provided in the comments is your best bet, because given that you don't care about your concrete Foo<'a> type, there's no need to give the method reference for that type.

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