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.
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 let
s.
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`
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With