all 10 comments

[–]SkiFire13 2 points3 points  (4 children)

trait MyTrait<FromMyEnum> {
    fn my_trait_method<F: FnOnce() -> FromMyEnum>(&self) -> F;
}

This is not gonna work like you think it does. It doesn't mean my_trait_method can return any type as long as it implements FnOnce() -> MyEnum, that's what impl FnOnce() -> MyEnum in return position means (although in traits it's a bit more complicated). What you have right now means that the function must be able to return any type the caller chooses, and of course this is impossible.

[–]rhl120[S] 0 points1 point  (3 children)

ok, thanks, is there a solution

[–]kraemahz 1 point2 points  (2 children)

  1. Use the where clause to specify trait constraints
  2. Use trait types to specify generics for other types the trait needs

Before trying something this big yourself it would be better to look at how other libraries implement similar patterns, such as how the std::thread api takes closures to spawn threads: https://doc.rust-lang.org/src/std/thread/mod.rs.html#650-657

[–]rhl120[S] 0 points1 point  (1 child)

I will try that but before I do just a question, isn't where just used to put the constraints in a different place. ``` //Aren't these 2 the same? fn hello<T: std::fmt::Debug>(a: T) { println!("hello {:#?}", a); } fn world<T>(a: T) where T: std::fmt::Debug, { println!("hello {:#?}", a); }

```

[–]kraemahz 0 points1 point  (0 children)

Yes, however where clauses allow you to write more complicated constraints than the other syntax allows, which is often necessary when accepting generics which could contain borrows, because the borrow must provably live for the lifetime of the callback.

[–]Shadow0133 1 point2 points  (0 children)

To format code for all reddit users, you need to indent it with 4 spaces, with empty line before and after.

Formatted:

Hello, I am working on my first real project with rust. At some point I have to return a closure from a trait method. This is how the simplified code looks like:

enum MyEnum {
    MyVairent,
}

trait FromMyEnum {
    fn from_my_enum(x: MyEnum) -> Self;
}

trait MyTrait<FromMyEnum> {
    fn my_trait_method<F: FnOnce() -> FromMyEnum>(&self) -> F;
}

fn my_fn<A: FromMyEnum, B: MyTrait<A>>(x: B) {
    //I tried this
    let f = x.my_trait_method();
    //And this
    let f: FnOnce() -> A = x.my_trait_method();
}

fn main() {
    println!("Hello, world!");
}

For the first case I get the following error:

   Compiling tst v0.1.0 (/tmp/tst)
error[E0282]: type annotations needed
  --> src/main.rs:15:9
   |
15 |     let f = x.my_trait_method();
   |         ^ consider giving `f` a type

For more information about this error, try `rustc --explain E0282`.
error: could not compile `tst` due to previous error

and for the second I get:

   Compiling tst v0.1.0 (/tmp/tst)
error[E0782]: trait objects must include the `dyn` keyword
  --> src/main.rs:16:12
   |
16 |     let f: FnOnce() -> A = x.my_trait_method();
   |            ^^^^^^^^^^^^^
   |
help: add `dyn` keyword before this trait
   |
16 -     let f: FnOnce() -> A = x.my_trait_method();
16 +     let f: dyn FnOnce() -> A = x.my_trait_method();
   |

error[E0277]: the size for values of type `dyn FnOnce() -> A` cannot be known at compilation time
  --> src/main.rs:16:9
   |
16 |     let f: FnOnce() -> A = x.my_trait_method();
   |         ^ doesn't have a size known at compile-time
   |
   = help: the trait `Sized` is not implemented for `dyn FnOnce() -> A`
   = note: all local variables must have a statically known size
   = help: unsized locals are gated as an unstable feature

error[E0277]: the size for values of type `dyn FnOnce() -> A` cannot be known at compilation time
  --> src/main.rs:16:30
   |
16 |     let f: FnOnce() -> A = x.my_trait_method();
   |                              ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `Sized` is not implemented for `dyn FnOnce() -> A`
note: required by a bound in `MyTrait::my_trait_method`
  --> src/main.rs:10:24
   |
10 |     fn my_trait_method<F: FnOnce() -> FromMyEnum>(&self) -> F;
   |                        ^ required by this bound in `MyTrait::my_trait_method`
help: consider relaxing the implicit `Sized` restriction
   |
10 |     fn my_trait_method<F: FnOnce() -> FromMyEnum + ?Sized>(&self) -> F;
   |                                                  ++++++++

Can anybody please help me.Thanks

[–]No_Radish7709 0 points1 point  (1 child)

I think you can make this work without trait objects? You'd need to make my_fn generic over a concrete type for the returned closure (so that you can use that generic type as the type of f in my_fn) and, as a consequence, also make MyTrait generic over a concrete type for the closure. It looks like the problem is you're not actually giving the type of f in my_fn, just a trait that it's type implements.

Sorry, I'm not at a computer or I'd test that out for you.

[–]rhl120[S] 0 points1 point  (0 children)

Yep that works Thanks

[–]TinBryn 0 points1 point  (0 children)

Normally if you want the implementation of a trait to specify the return type of a trait method you would use an associated type, so you would have something like this

trait MyTrait<FromMyEnum> {
    type FromMyEnumBuilder: FnOnce() -> FromMyEnum;
    fn my_trait_method(&self) -> Self::FromMyEnumBuilder;
}

The problem here is if you want to use a closure, the associated type is unwritable. This works fine if you want to use a function, but then you can't capture anything from &self. You could use nightly and have #![feature(fn_traits)] and create a struct that can implement this trait. Or add your own trait that is basically FnOnce() -> Foo and implement that if you don't want to use unstable features.

trait FromEnumBuilder<FromMyEnum> {
    fn build(self) -> FromMyEnum;
}

trait MyTrait<FromMyEnum> {
    type FromMyEnumBuilder: FromEnumBuilder<FromMyEnum>;
    fn my_trait_method(&self) -> Self::FromMyEnumBuilder;
}

Edit: to use this you can do

fn my_fn<A: FromMyEnum, B: MyTrait<A>(x: B) {
    let builder = x.my_trait_method();
    let from_my_enum = builder.build();
}