【问题标题】:How can I return the combination of two borrowed RefCells?如何返回两个借用的 RefCell 的组合?
【发布时间】:2019-06-26 17:16:58
【问题描述】:

我有一个包含两个Vecs 的结构,包裹在RefCells 中。我想在该结构上有一个方法,它结合两个向量并将它们作为新的RefCellRefMut 返回:

use std::cell::{RefCell, RefMut};

struct World {
    positions: RefCell<Vec<Option<Position>>>,
    velocities: RefCell<Vec<Option<Velocity>>>,
}

type Position = i32;
type Velocity = i32;

impl World {
    pub fn new() -> World {
        World {
            positions: RefCell::new(vec![Some(1), None, Some(2)]),
            velocities: RefCell::new(vec![None, None, Some(1)]),
        }
    }

    pub fn get_pos_vel(&self) -> RefMut<Vec<(Position, Velocity)>> {
        let mut poses = self.positions.borrow_mut();
        let mut vels = self.velocities.borrow_mut();

        poses
            .iter_mut()
            .zip(vels.iter_mut())
            .filter(|(e1, e2)| e1.is_some() && e2.is_some())
            .map(|(e1, e2)| (e1.unwrap(), e2.unwrap()))
            .for_each(|elem| println!("{:?}", elem));
    }
}

fn main() {
    let world = World::new();

    world.get_pos_vel();
}

如何将向量的压缩内容作为新的RefCell 返回?这可能吗?

我知道有RefMut::map(),我尝试将两个调用嵌套到map,但没有成功。

【问题讨论】:

  • 此外,您的.filter().map() 可能应该只是.filter_map 并避免unwraps。
  • 抱歉,当我发布这个时,我的大脑已经融化了。我提供了一个无法编译的最小示例,但我认为显示了我尝试做的事情。您链接的其他答案对这个特定问题没有帮助。
  • 我同意这个答案:如果你想返回一个新的Vec,它不需要包裹在任何Ref*中。你为什么认为你想要这样的签名?
  • @Shepmaster 这与Vec 无关。我想返回对这些 vecs 中元素的可变引用。而且我看不出有任何方法可以做到这一点。答案创建了一个新的Vec,其中包含新的复制元素,而不是引用。如果调用者更改返回的Vec 中的值,则World 中的该值不会更改。

标签: rust iterator borrowing


【解决方案1】:

您希望能够修改位置和速度。如果这些必须存储在两个单独的RefCells 中,那么绕过问题并使用回调进行修改呢?

use std::cell::RefCell;

struct World {
    positions: RefCell<Vec<Option<Position>>>,
    velocities: RefCell<Vec<Option<Velocity>>>,
}

type Position = i32;
type Velocity = i32;

impl World {
    pub fn new() -> World {
        World {
            positions: RefCell::new(vec![Some(1), None, Some(2)]),
            velocities: RefCell::new(vec![None, None, Some(1)]),
        }
    }

    pub fn modify_pos_vel<F: FnMut(&mut Position, &mut Velocity)>(&self, mut f: F) {
        let mut poses = self.positions.borrow_mut();
        let mut vels = self.velocities.borrow_mut();

        poses
            .iter_mut()
            .zip(vels.iter_mut())
            .filter_map(|pair| match pair {
                (Some(e1), Some(e2)) => Some((e1, e2)),
                _ => None,
            })
            .for_each(|pair| f(pair.0, pair.1))
    }
}

fn main() {
    let world = World::new();

    world.modify_pos_vel(|position, velocity| {
        // Some modification goes here, for example:
        *position += *velocity;
    });
}

【讨论】:

    【解决方案2】:

    如果你想返回一个新的Vec,那么你不需要把它包裹在RefMutRefCell中:

    基于您的代码,filtermap

    pub fn get_pos_vel(&self) -> Vec<(Position, Velocity)> {
        let mut poses = self.positions.borrow_mut();
        let mut vels = self.velocities.borrow_mut();
    
        poses.iter_mut()
            .zip(vels.iter_mut())
            .filter(|(e1, e2)| e1.is_some() && e2.is_some())
            .map(|(e1, e2)| (e1.unwrap(), e2.unwrap()))
            .collect()
    }
    

    替代filter_map

    poses.iter_mut()
        .zip(vels.iter_mut())
        .filter_map(|pair| match pair {
            (Some(e1), Some(e2)) => Some((*e1, *e2)),
            _ => None,
        })
        .collect()
    

    如果你真的想的话,你可以用RefCell::new 将它包装在RefCell 中,但我会让函数的用户将它包装在他们需要的任何东西中。

    【讨论】:

    • 现在我运行了你的代码,我发现了它为什么起作用:PositionVelocity 在我的示例中是 Copy,但我的实际类型不是。我也不想在那里进行复制。我想获得对这些值的真实引用,这就是为什么我想返回一种RefCell。这个函数的重点是调用者可以改变World里面的值。
    • 你应该决定你想要返回什么。包含引用的元组向量?迭代器可能会更好。
    • 只要调用者可以通过返回值改变World中的值,没关系。 Iterator, Vector, Tuple... 解决问题的方法。我尝试了很多使用RefMut::map 的方法,但都没有奏效。 RefMut::map_split 已在 1.35 中添加,并且与我正在尝试做的完全相反。像RefMut::merge 这样的东西在这里会有所帮助。
    猜你喜欢
    • 2015-07-28
    • 1970-01-01
    • 1970-01-01
    • 2021-04-27
    • 1970-01-01
    • 1970-01-01
    • 2021-06-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多