繁体   English   中英

如何将引用参数传递给盒装闭包?

[英]How do I pass reference parameters to boxed closures?

我想存储一个回调,它可以采用不同类型的参数(拥有值和引用),并且还可以修改其环境(因此是 FnMut)。 当使用引用调用回调时,我希望编译器强制该参数仅在闭包主体中有效。 我尝试使用盒装闭包来实现这一点。

一个最小的例子如下所示:

fn main() {
    let mut caller = Caller::new();
    let callback = |x: &Foo| println!("{:?}", x);
    caller.register(callback);
    
    let foo = Foo{
        bar: 1,
        baz: 2,
    };
    
    //callback(&foo);       // works
    caller.invoke(&foo);    // borrowed value does not live long enough

}

struct Caller<'a, T> {
    callback: Box<dyn FnMut(T) + 'a>
}

impl<'a, T> Caller<'a, T> {
    fn new() -> Self {
        Caller {
            callback: Box::new(|_| ()),
        }
    }
    
    fn register(&mut self, cb: impl FnMut(T) + 'a) {
        self.callback = Box::new(cb);
    }
    
    fn invoke(&mut self, x: T) {
        (self.callback)(x);
    }
}

#[derive(Debug, Clone)]
struct Foo {
    bar: i32,
    baz: i32,
}

我想了解为什么如果我直接调用callback()会起作用,但是如果我通过结构调用它而不是拥有闭包,编译器会抱怨生命周期。 也许它与Box有关? 如果我在caller之前定义foo ,我可以让它工作,但我想避免这种情况。

这是在使用类似类型的闭包和边界时编译器类型推断怪癖的另一个示例(问题 #41078 )。 尽管这个Caller<'a, T>似乎能够很好地处理给定泛型Tinvoke调用,但给定的示例传递了一个引用&'b Foo (其中'b将是该值的某个匿名生命周期)。 由于这个限制, T被推断为一个预期生命周期的&Foo ,这不同于任何生命周期对Foo类型值的引用( for<'a> &'a Foo ),并且与引用不兼容传递给invoke调用。

通过不将闭包传递给Caller ,编译器将能够正确推断回调的预期参数类型,包括引用生命周期。

解决此问题的一种方法是重新定义Caller以显式接收引用值作为回调参数。 如上所述,这会将推断类型&T的行为更改为更高级别的生命周期界限。

操场

fn main() {
    let mut caller = Caller::new();
    let callback = |x: &Foo| { println!("{:?}", x) };
    caller.register(callback);

    let foo = Foo { bar: 1, baz: 2 };

    caller.invoke(&foo);
}

struct Caller<'a, T> {
    callback: Box<dyn FnMut(&T) + 'a>,
}

impl<'a, T> Caller<'a, T> {
    fn new() -> Self {
        Caller {
            callback: Box::new(|_| ()),
        }
    }

    fn register(&mut self, cb: impl FnMut(&T) + 'a) {
        self.callback = Box::new(cb);
    }

    fn invoke(&mut self, x: &T) {
        (self.callback)(x);
    }
}

使这一点更清晰的一种方法是使用invoke的扩展定义:

    fn register<F>(&mut self, cb: F)
    where 
        F: for<'b> FnMut(&'b T) + 'a
    {
        self.callback = Box::new(cb);
    }

也可以看看:

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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