Opaque return type in trait

Hello,

I have two structs S1 and S2 and I want to write code that is generic and can use either. So I introduce a trait T. Alas, one method has an opaque return type (a closure). How can I do this? Thanks!

trait T {
    type F: Fn(String) -> String;
    fn f() -> Self::F;
}

struct S1 {}

impl T for S1 {
    // error[E0658]: `impl Trait` in type aliases is unstable
    type F = impl Fn(String) -> String;
    fn f() -> Self::F {
        |s| { format!("Yo! {}", s) }
    }
}

struct S2 {}

impl T for S2 {
    // error[E0658]: `impl Trait` in type aliases is unstable
    type F = impl Fn(String) -> String;
    fn f() -> Self::F {
        |s| { format!("Hey! {}", s) }
    }
}

Best, Oliver

1 Like

You can return a trait object: Box<dyn Fn(String) -> String>

If neither of them captures any values in their closure you can use fn(String) -> String which will do effectively the same thing but without allocating a Box.

1 Like

Box won't actually allocate anything for a zero-sized type, including a non-capturing closure. However, the immediate Box<dyn Fn> value will still be "fat", with both object and vtable pointers, whereas fn is a single function pointer.

5 Likes

Thanks! I was hoping to return unboxed closures, but if that's not possible, then I guess that's what it is.

simple try

struct S1{}
impl S1{
    fn s()->impl Fn(String) -> String{
        |s| { format!("Hey! {}", s) }
    }
}
fn main(){
    let a=S1::s();
    println!("{}",a("c".to_string()));
}

I'll try whether it works for traits.


unfortunately, such thing does not work in stable rust,
but your code works perfectly in nightly rust, just adding a line:

#![feature(type_alias_impl_trait)]

Why not just make a struct T, and use

T( |s| { format!("Yo! {}", s) })

to get S1?

code example: you could find that, s11,s1 and s2

#[derive(Clone)]
struct T<F> where F:Fn(String)->String{
    data:i32,
    func:F
}
fn main(){
    let s1f=|s| { format!("Yo! {}", s) };
    let s2f=|s| { format!("Hey! {}", s) };
    let mut s1=T{data:1,func:s1f};
    let s2=T{data:2,func:s2f};
    let s11=s1.clone();
    s1.data=3;
    println!("{} {} {}",(s11.func)("s11".to_string()),(s1.func)("s1".to_string()),(s2.func)("s2".to_string()));
    println!("{} {} {}",s11.data,s2.data,s1.data);
    println!("{} {} {}",std::mem::size_of_val(&s11),std::mem::size_of_val(&s1),std::mem::size_of_val(&s2));
}

which generates

Yo! s11 Yo! s1 Hey! s2
1 2 3
4 4 4

suggests that func is a ZST, not a boxed type.