使用Rc <fnmut>作为FnMut

时间:2018-01-21 02:55:49

标签: rust

给定Rc<FnMut>,如何在迭代器上映射时使用它?例如:

use std::rc::Rc;

fn main() {
    let f = Rc::new(|x| x);
    let v = vec![1, 2, 3];
    let _: Vec<_> = v.into_iter().map(f).collect();
}

playground

给出错误

error[E0277]: the trait bound `std::rc::Rc<[closure@src/main.rs:6:21: 6:26]>: std::ops::FnMut<({integer},)>` is not satisfied
 --> src/main.rs:8:35
  |
8 |     let _: Vec<_> = v.into_iter().map(f).collect();
  |                                   ^^^ the trait `std::ops::FnMut<({integer},)>` is not implemented for `std::rc::Rc<[closure@src/main.rs:6:21: 6:26]>`

error[E0599]: no method named `collect` found for type `std::iter::Map<std::vec::IntoIter<{integer}>, std::rc::Rc<[closure@src/main.rs:6:21: 6:26]>>` in the current scope
 --> src/main.rs:8:42
  |
8 |     let _: Vec<_> = v.into_iter().map(f).collect();
  |                                          ^^^^^^^
  |
  = note: the method `collect` exists but the following trait bounds were not satisfied:
          `std::iter::Map<std::vec::IntoIter<{integer}>, std::rc::Rc<[closure@src/main.rs:6:21: 6:26]>> : std::iter::Iterator`
          `&mut std::iter::Map<std::vec::IntoIter<{integer}>, std::rc::Rc<[closure@src/main.rs:6:21: 6:26]>> : std::iter::Iterator`

我认为一个简单的*f可以解决问题,但后来我遇到cannot move out of borrowed content错误。

我最接近的是:

use std::rc::Rc;

fn main() {
    let mut f = Rc::new(|x| x);
    let v = vec![1, 2, 3];
    let _: Vec<_> = v.into_iter().map(Rc::get_mut(&mut f).unwrap()).collect();
}

playground

2 个答案:

答案 0 :(得分:2)

直接传递f并不起作用,因为Rc<T> where T: FnMut没有实现FnMut

如果你的闭包没有执行任何变异(即它实现了Fn),你可以改为编写&*f。这是因为:

  1. Rc实现了Deref,它改变了*运算符的行为,使*x扩展为*(x.deref()),让我们可以访问包装的闭包。
  2. *运算符生成左值,允许您对结果进行引用
  3. 对实施Fn implement FnMut的类型的不可变引用。
  4. use std::rc::Rc;
    
    fn main() {
        let f = Rc::new(|x| x);
        let v = vec![1, 2, 3];
        let _: Vec<_> = v.into_iter().map(&*f).collect();
    }
    

    但是,&mut *f如果闭包存储在Rc中,则无法正常工作,因为Rc没有实现DerefMut,因此不会{&}让我们可以借用它的内部。

    另一个选择是传递一个调用fmap的闭包。

    use std::rc::Rc;
    
    fn main() {
        let f = Rc::new(|x| x);
        let v = vec![1, 2, 3];
        let _: Vec<_> = v.into_iter().map(|v| f(v)).collect();
    }
    

    Deref coercions are also applied on method calls.虽然它看起来不像一个,f(v)实际上扩展(从Rust 1.23开始)到f.call((v,))(请注意,这种语法不稳定,因此只是每晚一次如果启用了fn_traits功能,编译器将接受它。

    如果闭包位于Box,那么您可以写&mut *f,因为Box实现了DerefMut

    fn main() {
        let mut f = Box::new(|x| x);
        let v = vec![1, 2, 3];
        let _: Vec<_> = v.into_iter().map(&mut *f).collect();
    }
    

答案 1 :(得分:0)

啊! try_unwrap将允许您移动对闭包的访问权限,而*f将为您提供对闭包的引用。

所以解决方案可能是:

use std::rc::Rc;

fn main() {
    let f = Rc::new(|x| x);
    let v = vec![1, 2, 3];
    match Rc::try_unwrap(f) {
        Ok(f2) => {
            let _: Vec<_> = v.into_iter().map(f2).collect();
        }
        _ => ()
    }
}

(虽然这可能是一种更清洁的方式)