Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

expected trait core::ops::FnMut, found type parameter

Tags:

rust

I don't understand why the code below does not compile. It seems like rust is just not 'expanding' the type parameter, since it looks like it matches to me.

Code (rust playpen: http://is.gd/gC82I4)

use std::sync::{Arc, Mutex};

struct Data{
    func: Option<Box<FnMut(String) + Send>>
}

fn newData<F>(func: Option<Box<F>>) -> Data
where F: FnMut(String) + Send{
    Data{
        func: func
    }
}

fn main(){
    let _ = newData(Some(Box::new(|msg|{})));
}

Error

<anon>:10:15: 10:19 error: mismatched types:
 expected `core::option::Option<Box<core::ops::FnMut(collections::string::String) + Send>>`,
    found `core::option::Option<Box<F>>`
(expected trait core::ops::FnMut,
    found type parameter) [E0308]
<anon>:10         func: func
                        ^~~~
error: aborting due to previous error
playpen: application terminated with error code 101
like image 510
Nathan Fox Avatar asked Feb 11 '23 08:02

Nathan Fox


2 Answers

You need to help rust by spelling out the cast from Box<F> to Box<FnMut> at least partly.

Because Box<Trait> implies Box<Trait + 'static>, you need to add the bound F: 'static too.

struct Data {
    func: Option<Box<FnMut(String) + Send>>
}

fn new_data<F>(func: Option<Box<F>>) -> Data where
    F: FnMut(String) + Send + 'static
{
    Data {
        func: func.map(|x| x as Box<_>)
    }
}

fn main() {
    let _ = new_data(Some(Box::new(|msg|{ })));
}

To note here is that Box<F> and Box<FnMut ...> are not the same type, but the former will convert to the latter automatically in most cases. Inside the Option here we just needed to help the conversion by writing an explicit cast.

like image 74
bluss Avatar answered Feb 19 '23 18:02

bluss


While the answer by user139873 is absolutely correct, I'd like to add that it is more idiomatic to pass the closure into the function by value and box it in the function:

struct Data {
    func: Option<Box<FnMut(String) + Send>>
}

fn new_data<F>(func: Option<F>) -> Data where
        F: FnMut(String) + Send + 'static {
    Data {
        func: func.map(|f| Box::new(f) as Box<_>)
    }
}

fn main() {
    let _ = new_data(Some(|msg| {}));
}

This way you place fewer restrictions on the caller, and their code becomes simpler.

like image 26
Vladimir Matveev Avatar answered Feb 19 '23 17:02

Vladimir Matveev