簡體   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