简体   繁体   中英

Does `Box` obscure trait bounds? Why can i assign `dyn FnOnce()` to `dyn Fn()` in this case?

I'd like to have a struct, Quox which can optionally have each type of closure that there can be( Fn , FnMut and FnOnce ) around. I typed it this way:

#[derive(Default, Debug)]
pub struct Quox<Tfn: Fn(&Person) + Sized, Tfnmut: FnMut(&mut Person) + Sized, Tfnonce: FnOnce(&Person) + Sized> {
    soft_closure: Option<Tfn>,
    mut_closure : Option<Tfnmut>,
    move_closure: Option<Tfnonce>,
}

Now, when instantiating it i do it via Box because Rust complains the trait Sized is not implemented for `dyn Fn()`rustcE0277 . (Maybe there is a better way or i misunderstand trait objects?):

pub fn main(){
    let mut qx = Quox::<Box<dyn Fn(&Person)>, Box<dyn FnMut(&mut Person)>, Box<dyn FnOnce(&Person)>> {
        move_closure: None,
        mut_closure : None,
        soft_closure: None,
    };
    ...

}

  1. What really perplexes me further is when i create the corresponding types of closures -- why i can assign them willy-nilly to the fields inside qx :

pub fn main(){
    let mut qx = Quox::<Box<dyn Fn(&Person)>, Box<dyn FnMut(&mut Person)>, Box<dyn FnOnce(&Person)>> {
        move_closure: None,
        mut_closure : None,
        soft_closure: None,
    };


    let greeting  = "Hi. We are moving soon!".to_string();
    let clsr_move = move |p:&Person| println!("Hi, {}. {} says: {}", p.name, p.name, greeting);
    let only_a_ref_clsr = |p:&Person| println!("Hi, {}.",p.name );

 
    // qx.move_closure = Some(Box::new(clsr_move));        // expected
    // qx.soft_closure = Some(Box::new(only_a_ref_clsr));  // expected

    qx.soft_closure = Some(Box::new(clsr_move));           // Why is this valid???
    qx.move_closure = Some(Box::new(only_a_ref_clsr));     // Ok Fn is supertrait to FnOnce

}

Again, what's confusing is that i'm getting no error for trying to assign a (seemingly) FnMut to Fn .


  1. Furthermore... why assigning the Box to an intermediary variable now throws error???

pub fn main(){
    let mut qx = Quox::<Box<dyn Fn(&Person)>, Box<dyn FnMut(&mut Person)>, Box<dyn FnOnce(&Person)>> {
        move_closure: None,
        mut_closure : None,
        soft_closure: None,
    };


    let greeting  = "Hi. We are moving soon!".to_string();
    let clsr_move = move |p:&Person| println!("Hi, {}. {} says: {}", p.name, p.name, greeting);
    let only_a_ref_clsr = |p:&Person| println!("Hi, {}.",p.name );

    let someboxmove = Some(Box::new(only_a_ref_clsr));
    qx.soft_closure = someboxmove;                         // Invalid. 

}

The error i'm getting is:

let someboxmove: Option<Box<|&Person| -> ()>>
Go to Option | Box | Person | Global

mismatched types
expected enum `Option<Box<dyn for<'r> Fn(&'r traitobjects::Person)>>`
   found enum `Option<Box<[closure@src/traitobjects.rs:49:27: 49:66]>>`rustcE0308
traitobjects.rs(49, 27): the found closure
traitobjects.rs(66, 5): expected due to the type of this binding

Boiling down your first question:

let greeting  = "Hi. We are moving soon!".to_string();
let clsr_move = move |p:&Person| println!("Hi, {}. {} says: {}", p.name, p.name, greeting);

let soft_closure: Box<dyn Fn(&Person)> = Some(Box::new(clsr_move));

There's no reason that clsr_move can't be Fn . The move is for telling the compiler to move all captures into the closure, but what makes a closure strictly FnOnce is if variables were moved out of the closure. This closure can obviously be called again and again.


Your second problem is simply a side-effect of Rust's type inference. If the type that Box::new() should create is not immediately clear from the expression, then it will pick the type that was given to it; in the latter case the anonymous type of the closure. In the former, it can see that it is going to be assigned to something expecting a Box<dyn...> and can preemptively coerce the type. So its simply the case that the inference algorithm does not look far enough ahead.

It also can't implicitly coerce a Option<Box<T>> into a Option<Box<dyn Trait>> . It could coerce a Box<T> into a Box<dyn Trait> , but since these have different sizes and layouts it doesn't extend as a generic parameter for Option

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM