有没有办法(这样或那样)创建一个拥有版本的(盒)关闭生 rust ?

wbgh16ku  于 2023-01-13  发布在  其他
关注(0)|答案(2)|浏览(107)

我想写一个FuncWrapper结构体,其中包含一个new函数,该函数以一个(Boxed)闭包作为参数,并返回一个装饰闭包,该闭包只是向传入闭包添加一些样板。但我也希望返回值是“owned”的,以允许以下情况(例如):

fn main() {                                                                                                                                                                                                                           
                                                                                                                                                                                                                                      
    let a: FuncWrapper<u32>;                                                                                                                                                                                                          
                                                                                                                                                                                                                                      
    {                                                                                                                                                                                                                                 
        let foo = |x: u32| {print!("{}", x)};                                                                                                                                                                                         
        let b = &Box::new(foo);                                                                                                                                                                                                       
        a = FuncWrapper::new(b);                                                                                                                                                                                                     
    }                                                                                                                                                                                                                                 
                                                                                                                                                                                                                                      
    let _c = (a.func)(42);                                                                                                                                                                                                            
}

也就是说,我希望new的返回值是一个“owned”值。
现在我最近了解到Rust(Trais)中的所有闭包都必须有一个与它们相关联的生命周期(如果没有指定,它将默认为'static)。但我认为,返回值new的FuncWrapper中闭包的任何生命周期似乎都是错误的。
我不希望生存期是传入引用的生存期,因为这将太短。例如,下面的代码 * 不会 * 工作,因为返回值的生存期是参数f_box_ref的生存期,在我提供的上述情况下,它的生存期不够长

struct FuncWrapper<'b, DomainType> {                                                                                                                                                                                                  
    func: Box<dyn Fn(DomainType) + 'b>                                                                                                                                                                                                
}                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                      
impl<'b, DomainType> FuncWrapper<'b, DomainType> {                                                                                                                                                                                    
    fn new<F: Fn(DomainType) + 'static>(f_box_ref: &'b Box<F>) -> FuncWrapper<'b, DomainType> {                                                                                                                                       
        let new_f = move |a: DomainType| {                                                                                                                                                                                            
            // ..add some boilerplate then                                                                                                                                                                                            
            (**f_box_ref)(a)                                                                                                                                                                                                          
        };                                                                                                                                                                                                                            
        let b = Box::new(new_f);                                                                                                                                                                                                      
        FuncWrapper {func: b}                                                                                                                                                                                                         
    }                                                                                                                                                                                                                                 
}

导致

error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:22:18
   |
22 |         let b = &Box::new(foo);
   |                  ^^^^^^^^^^^^^ creates a temporary which is freed while still in use
23 |         a = FuncWrapper::new(b);
24 |     }
   |     - temporary value is freed at the end of this statement
...
27 | }
   | - borrow might be used here, when `a` is dropped and runs the destructor for type `FuncWrapper<'_, u32>`
   |
   = note: consider using a `let` binding to create a longer lived value

将返回参数的生存期改为'static似乎也是错误的,因为我的目标是 * 在new函数'* 中创建一个新闭包,(那么它怎么可能是静态的呢?)

struct FuncWrapper<DomainType> {                                                                                                                                                                                                      
    func: Box<dyn Fn(DomainType) + 'static>                                                                                                                                                                                           
}                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                      
impl<DomainType> FuncWrapper<DomainType> {                                                                                                                                                                                            
    fn new<'b, F: Fn(DomainType) + 'static>(f_box_ref: &'b Box<F>) -> FuncWrapper<DomainType> {                                                                                                                                       
        let new_f = move |a: DomainType| {                                                                                                                                                                                            
            // ..add some boilerplate then                                                                                                                                                                                            
            (**f_box_ref)(a)                                                                                                                                                                                                          
        };                                                                                                                                                                                                                            
        let b = Box::new(new_f);                                                                                                                                                                                                      
        FuncWrapper {func: b}                                                                                                                                                                                                         
    }                                                                                                                                                                                                                                 
}

错误

error[E0759]: `f_box_ref` has lifetime `'b` but it needs to satisfy a `'static` lifetime requirement
  --> src/main.rs:7:21
   |
6  |       fn new<'b, F: Fn(DomainType) + 'static>(f_box_ref: &'b Box<F>) -> FuncWrapper<DomainType> {
   |                                                          ---------- this data with lifetime `'b`...
7  |           let new_f = move |a: DomainType| {
   |  _____________________^
8  | |             // ..add some boilerplate then
9  | |             (**f_box_ref)(a)
10 | |         };
   | |_________^ ...is used here...
11 |           let b = Box::new(new_f);
12 |           FuncWrapper {func: b}
   |                              - ...and is required to live as long as `'static` here

这个错误让我很吃惊,因为我以为move的任务是按值捕获,一开始我以为我需要先取消引用f_box_ref,但是

impl<DomainType> FuncWrapper<DomainType> {                                                                                                                                                                                            
    fn new<'b, F: Fn(DomainType) + 'static>(f_box_ref: &'b Box<F>) -> FuncWrapper<DomainType> {                                                                                                                                       
        let f_box: Box<F> =  *f_box_ref;                                                                                                                                                                                              
        let new_f = move |a: DomainType| {                                                                                                                                                                                            
            // ..add some boilerplate then                                                                                                                                                                                            
            (*f_box)(a)                                                                                                                                                                                                               
        };                                                                                                                                                                                                                            
        let b = Box::new(new_f);                                                                                                                                                                                                      
        FuncWrapper {func: b}                                                                                                                                                                                                         
    }                                                                                                                                                                                                                                 
}

导致

error[E0507]: cannot move out of `*f_box_ref` which is behind a shared reference
 --> src/main.rs:7:30
  |
7 |         let f_box: Box<F> =  *f_box_ref;
  |                              ^^^^^^^^^^
  |                              |
  |                              move occurs because `*f_box_ref` has type `Box<F>`, which does not implement the `Copy` trait
  |                              help: consider borrowing here: `&*f_box_ref`

唉,按照提示“帮助:考虑借用以下内容:&*f_box_ref“也没有帮助

impl<DomainType> FuncWrapper<DomainType> {                                                                                                                                                                                            
    fn new<'b, F: Fn(DomainType) + 'static>(f_box_ref: &'b Box<F>) -> FuncWrapper<DomainType> {                                                                                                                                       
        let f_box: &Box<F> =  &*f_box_ref;                                                                                                                                                                                            
        let new_f = move |a: DomainType| {                                                                                                                                                                                            
            // ..add some boilerplate then                                                                                                                                                                                            
            (*f_box)(a)                                                                                                                                                                                                               
        };                                                                                                                                                                                                                            
        let b = Box::new(new_f);                                                                                                                                                                                                      
        FuncWrapper {func: b}                                                                                                                                                                                                         
    }                                                                                                                                                                                                                                 
}

错误

error[E0759]: `f_box_ref` has lifetime `'b` but it needs to satisfy a `'static` lifetime requirement
  --> src/main.rs:7:31
   |
6  |     fn new<'b, F: Fn(DomainType) + 'static>(f_box_ref: &'b Box<F>) -> FuncWrapper<DomainType> {
   |                                                        ---------- this data with lifetime `'b`...
7  |         let f_box: &Box<F> =  &*f_box_ref;
   |                               ^^^^^^^^^^^ ...is used here...
...
13 |         FuncWrapper {func: b}
   |                            - ...and is required to live as long as `'static` here

所以很明显,我们仍然没有返回任何类似于新拥有的闭包的东西,并且对返回的闭包的生命周期的任何选择似乎都是武断和错误的。
我想做的事应该是很平常的事。我错过了什么?

2ul0zpep

2ul0zpep1#

这是相当直接的,要从引用中获得一个拥有的类型,你有3个选项可以立即想到:CopyCloneToOwnedToOwned更高级一点,因为它也可以更改类型,而且由于Copy需要Cloned,我将重点介绍后面的内容。
要从&Box<Fn>获取拥有的Fn,只需调用clone()

impl<DomainType> FuncWrapper<DomainType> {
    fn new<F: Fn(DomainType) + Clone + 'static>(f_box_ref: &Box<F>) -> FuncWrapper<DomainType> {
        let f_box = f_box_ref.clone();
        let new_f = move |a: DomainType| {
            // ..add some boilerplate then
            (f_box)(a)
        };
        let b = Box::new(new_f);
        FuncWrapper { func: b }
    }
}
hxzsmxv2

hxzsmxv22#

因为它不是FnMut,而只是Fn,也许您可以使用Rc来代替Box?您始终可以克隆Rc

use std::rc::Rc;

impl<DomainType> FuncWrapper<DomainType> {
    fn new<F: Fn(DomainType) + 'static>(f_rc: Rc<F>) -> FuncWrapper<DomainType> {
        let new_f = move |a: DomainType| {
            // ..add some boilerplate then
            (f_rc)(a)
        };
        let b = Box::new(new_f);
        FuncWrapper { func: b }
    }
}

相关问题