Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implement trait for closure type alias

I have this closure type alias:

type ClosureType = Box<Fn(i32) -> i32>;

this trait:

trait Trait {
    fn change(&self, y: i32) -> i32;
}

and these functions:

fn with_one(x: Box<Fn(i32) -> i32>) -> i32 {
    x(1)
}

fn plus_one(x: i32) -> i32 {
    x+1
}

fn main() {
    let a = Box::new(|x: i32|{x+1});
    let b: ClosureType = Box::new(|x: i32|{x+1});
    let c = Box::new(plus_one);
    let d: ClosureType = Box::new(plus_one);
    println!("{}", a.change(1));
    println!("{}", b.change(1));
    println!("{}", c.change(1));
    println!("{}", d.change(1));
    println!("{}", with_one(a));
    println!("{}", with_one(b));
    println!("{}", with_one(c));
    println!("{}", with_one(d));
}

When I implement the trait Trait for ClosureType or for Box<Fn(i32) -> i32> which is the same if I understand correctly type aliases:

impl Trait for ClosureType {
    fn change(&self, y: i32) -> i32{
        self(y)
    }
}

or

impl Trait for Box<Fn(i32) -> i32> {
    fn change(&self, y: i32) -> i32{
        self(y)
    }
}

for variable a I get:

<anon>:32:22: 32:31 error: no method named `change` found for type
`Box<[closure <anon>:28:22: 28:35]>` in the current scope 
<anon>:32     println!("{}", a.change(1));

and for variable c I get:

<anon>:34:22: 34:31 error: no method named `change` found for type
`Box<fn(i32) -> i32 {plus_one}>` in the current scope
<anon>:34     println!("{}", c.change(1));

However variables a and c are accepted from function with_one(x: Box<Fn(i32) -> i32>) -> i32, in other words it seems that they have the same type(Box<Fn(i32) -> i32>) for function with_one but different(Box<[closure <anon>:24:22: 24:35]> and Box<fn(i32) -> i32 {plus_one}) for Trait implementation.

I feel I missing something here but not sure what it is, could you enlighten me?

you can find all the code in this rust playground.

like image 802
Otobo Avatar asked Aug 26 '15 12:08

Otobo


1 Answers

I believe this happens due to automatic coercion (that is, its absence) from a concrete type to a trait object type.

When you call with_one(), the compiler is able to understand from the function argument type that you want a trait object and so it inserts automatic coercions:

with_one(a as Box<Fn(i32) -> i32>);
with_one(c as Box<Fn(i32) -> i32>);

For b and d these coercions have already happened at their assignment place in lets.

For trait methods, however, the compiler does not perform coercions. This is a common behavior around generics (and traits are implemented over generics - their Self type is essentially an implicit type parameter for all trait methods). For example, Rust also does not perform deref coercions when using generics:

trait MyStringLike {}

impl<'a> MyStringLike for &'a str {}

fn function<T: MyStringLike>(t: T) {}

let s: String = "abcde".into();
function(&s);  // the trait `main::MyStringLike` is not implemented for the type `&collections::string::String`
like image 106
Vladimir Matveev Avatar answered Oct 16 '22 17:10

Vladimir Matveev