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()),
}
}
}