Collections 如何根据集合中的消息在集合之间对mpsc::Receivers进行排序?

Collections 如何根据集合中的消息在集合之间对mpsc::Receivers进行排序?,collections,rust,Collections,Rust,我有一个包含std::sync::mpsc::Receiver的集合,希望根据其中的消息将它们排序到其他集合中 我迭代到接收者,测试他们是否收到消息,如果收到,我测试消息的性质,并根据它,将接收者移动到另一个集合: for receiver in receivers.iter() { if let Ok(msg) = receiver.try_recv() { match msg { Message::Foo => {

我有一个包含
std::sync::mpsc::Receiver
的集合,希望根据其中的消息将它们排序到其他集合中

我迭代到接收者,测试他们是否收到消息,如果收到,我测试消息的性质,并根据它,将接收者移动到另一个集合:

for receiver in receivers.iter() {
    if let Ok(msg) = receiver.try_recv() {
        match msg {
            Message::Foo => {
                // TODO remove receiver from receivers
                foos.push(receiver);
            },
            Message::Bar => {
                // TODO remove receiver from receivers
                bars.push(receiver);
            },
        }
    }
}
我不知道如何从
receivers
集合中删除
receiver
。因为我在迭代时借用它作为不可变的,所以我不能修改它而不遇到编译错误

我觉得我没有使用正确的生锈工具来完成这个非常基本的算法

为了说明我的问题,假设我将彩色球从集合A分类到集合B和C,但我不能克隆这些球,我只能在取球时看到球的颜色,这会破坏我取下一个球时的颜色

将球从数字0标记到数字6,将其颜色写在索引旁边,然后将球放入好的集合中,这样的解决方案很不方便。

您可以使用


要将丢弃的项目移动到其他向量,您可以编写自己的
retain
函数。下面的算法将以迭代器的形式返回丢弃的项。不能保证订购后会退回。不幸的是,它不支持将通道移动到多个其他向量

pub fn retain2<T, F>(vec: &mut Vec<T>, mut f: F) -> impl Iterator<Item = T> + '_
where
    F: FnMut(&mut T) -> bool,
{
    let len = vec.len();
    let mut del = 0;
    {
        let v = &mut **vec;

        for i in 0..len {
            if !f(&mut v[i]) {
                del += 1;
            } else if del > 0 {
                v.swap(i - del, i);
            }
        }
    }
    vec.drain(len - del ..)
}

或者,您可以准确地获得问题中描述的行为,但这无法使用标准库中的工具安全地完成

为了正确处理恐慌,我们可以引入一种防护类型:

struct RetainGuard<'a, T> {
    vec: &'a mut Vec<T>,
    // How many items have been taken?
    taken: usize,
    // How many items have been inserted again?
    // This must never be larger than `taken`.
    inserted: usize,
    // The original length of the vector.
    vec_len: usize,
}
struct-RetainGuard{
fn获取(&mut self)->选项{
让我=自取;
如果i==self.vec_len{
不返回任何值;
}
self.take+=1;
不安全{
一些(std::ptr::read(&mut self.vec[i]))
}
}
//安全:
//插入的项目不得超过已采取的项目。
不安全的fn插入(&mut self,val:T){
std::ptr::write(&mut self.vec[self.inserted],val);
自插入+=1;
调试_断言!(自插入){
fn下降(&mut自我){
不安全{
自向量集(自插入);
//除非关闭恐慌,否则这是一片空白。
std::ptr::将_放入_位置(self.not_take_slice());
}
}
}
恳求{
fn未采取措施\u切片(&mut self)->&mut[T]{
不安全{
设ptr=self.vec.as_mut_ptr();
让开始=ptr.add(self.take);
设len=self.vec_len-self.take;
std::slice::from_raw_parts_mut(开始,len)
}
}
}
最后,我们可以使用我们的保护来实现以下方法

pub fn retain3<T, F>(vec: &mut Vec<T>, mut f: F)
where
    F: FnMut(T) -> Option<T>,
{
    let mut guard = RetainGuard::new(vec);
    while let Some(item) = guard.take() {
        if let Some(item) = f(item) {
            unsafe {
                // SAFETY: We have called take more times than insert.
                guard.insert(item);
            }
        }
    }
}
pub fn retain3(vec:&mut vec,mut f:f)
哪里
F:FnMut(T)->选项,
{
让mut-guard=RetainGuard::new(vec);
而让一些(物品)=守卫。带走(){
如果让一些(项目)=f(项目){
不安全{
//安全:我们打电话的次数比插入次数多。
防护装置。插入(项目);
}
}
}
}

使用此方法,实现所需的功能应该非常简单。请参阅完整代码。

您可以使用
Vec::drain
将项目移出向量。这将移动所有项目,但我们可以通过将其临时移动到新的
Vec
来处理要保留的项目

fn distribute(receivers: &mut Vec<Receiver<Message>>) -> (Vec<Receiver<Message>>, Vec<Receiver<Message>>) {
    let mut foos: Vec<Receiver<Message>> = Vec::new();
    let mut bars: Vec<Receiver<Message>> = Vec::new();
    let mut go_back: Vec<Receiver<Message>> = Vec::new();
    for receiver in receivers.drain(..) {
        if let Ok(msg) = receiver.try_recv() {
            match msg {
                Message::Foo => &mut foos,
                Message::Bar => &mut bars,
                _ => &mut go_back,
            }.push(receiver);
        }
    }
    *receivers = go_back;
    (foos, bars)
}
fn分发(接收者:&mut Vec)->(Vec,Vec){
让mut-foos:Vec=Vec::new();
让mut条:Vec=Vec::new();
让mut返回:Vec=Vec::new();
用于接收器中的接收器。排水管(…){
如果让Ok(msg)=接收器。请尝试{
配味精{
消息::Foo=>&多个foos,
消息::条=>&多条,
_=>返回(&U),
}.推(接收器);
}
}
*接收器=返回;
(食物、酒吧)
}
当然,这会每次分配一个新的向量并调整其大小;一个小小的改进是在两个向量之间交换,而不是每次分配一个。我认为尚未稳定的
Vec::drain_filter
可能会有所帮助,但该过滤器只能返回
bool
,因此它无法告知将项目移动到哪里


理想情况下,您可以将
Vec::retain
(最初由@AliceRyhl显示,但后来编辑为包含更复杂的方法)与横梁通道(即
Clone
)或
Rc
)相结合,后者也是(普通的)可关闭,但运行时成本和堆分配微乎其微。如果这些都是不可接受的,我建议使用两种变体:

[✓] 变体1:假设集合是一个向量,使用索引进行迭代。它不是非常优雅,但也不是那么糟糕,它以最小的麻烦和任何人都能理解的代码完成工作:

// [✓] this is the accepted variant
let mut pos = 0;
while pos < receivers.len() {
    if let Ok(msg) = receivers[pos].try_recv() {
        match msg {
            Message::Foo => foos.push(receivers.remove(pos)),
            Message::Bar => bars.push(receivers.remove(pos)),
            _ => pos += 1,
        }
    } else {
        pos += 1;
    }
}

如果
None
s仍然存在于
receivers
中是一个问题,请使用类似于
receivers.retain(Option::is_-some)

的方法删除它们。这篇博文有点相关:我尝试了这个方法,得到了
expected struct std::sync::mpsc::Receiver,找到了参考
“retain”获取接收器上的引用,然后无法将其推送到需要接收器的集合。我是否应该使用
*
解除对它的引用?啊,因此您需要将接收器移动到其他地方…这比较困难。我建议改为使用频道,因为它的接收器可以克隆。然后您可以克隆
接收器
,并将该克隆移动到另一个集合中。顺便说一句,我非常喜欢你的博客文章。
retain2
变体应该如何工作?OP显然希望将接收器移动到
foos
impl<'a, T> Drop for RetainGuard<'a, T> {
    fn drop(&mut self) {
        unsafe {
            self.vec.set_len(self.inserted);
            // This is an empty slice unless the closure panics.
            std::ptr::drop_in_place(self.not_taken_slice());
        }
    }
}
impl<'a, T> RetainGuard<'a, T> {
    fn not_taken_slice(&mut self) -> &mut [T] {
        unsafe {
            let ptr = self.vec.as_mut_ptr();
            let start = ptr.add(self.taken);
            let len = self.vec_len - self.taken;
            std::slice::from_raw_parts_mut(start, len)
        }
    }
}
pub fn retain3<T, F>(vec: &mut Vec<T>, mut f: F)
where
    F: FnMut(T) -> Option<T>,
{
    let mut guard = RetainGuard::new(vec);
    while let Some(item) = guard.take() {
        if let Some(item) = f(item) {
            unsafe {
                // SAFETY: We have called take more times than insert.
                guard.insert(item);
            }
        }
    }
}
fn distribute(receivers: &mut Vec<Receiver<Message>>) -> (Vec<Receiver<Message>>, Vec<Receiver<Message>>) {
    let mut foos: Vec<Receiver<Message>> = Vec::new();
    let mut bars: Vec<Receiver<Message>> = Vec::new();
    let mut go_back: Vec<Receiver<Message>> = Vec::new();
    for receiver in receivers.drain(..) {
        if let Ok(msg) = receiver.try_recv() {
            match msg {
                Message::Foo => &mut foos,
                Message::Bar => &mut bars,
                _ => &mut go_back,
            }.push(receiver);
        }
    }
    *receivers = go_back;
    (foos, bars)
}
// [✓] this is the accepted variant
let mut pos = 0;
while pos < receivers.len() {
    if let Ok(msg) = receivers[pos].try_recv() {
        match msg {
            Message::Foo => foos.push(receivers.remove(pos)),
            Message::Bar => bars.push(receivers.remove(pos)),
            _ => pos += 1,
        }
    } else {
        pos += 1;
    }
}
for receiver in receivers.iter_mut() {
    if receiver.is_none() {
        continue;
    }
    if let Ok(msg) = receiver.as_ref().unwrap().try_recv() {
        match msg {
            Message::Foo => foos.push(receiver.take().unwrap()),
            Message::Bar => bars.push(receiver.take().unwrap()),
        }
    }
}