这是我希望工作的一些不言自明的代码:
type some_t = i32;
struct SomeStruct {
pub some_tuple_vector: Vec<(some_t, some_t)>,
}
impl SomeStruct {
fn some_method(&mut self) {
for p in self.some_tuple_vector.iter_mut() {
match p {
(x, y) if x < 0 => self.some_tuple_vector.remove(p),
(x, y) => p = (x, y - 1),
}
}
}
}
fn main() {}
但是,我试图匹配和解构元组的两行都出错:
error[E0308]: mismatched types
--> src/main.rs:12:17
|
12 | (x, y) if x < 0 => self.some_tuple_vector.remove(p),
| ^^^^^^ expected mutable reference, found tuple
|
= note: expected type `&mut (i32, i32)`
found type `(_, _)`
我不明白;不是类型&mut (i32,i32)
一个元组本身吗?
我意识到我可以重写这段代码更优雅:
self.some_tuple_vector = self.some_tuple_vector
.iter()
.map(|(x, y)| (x - 1, y))
.filter(|(x, y)| x > 0);
但是我得到了同样的错误:
error[E0308]: mismatched types
--> src/main.rs:12:19
|
12 | .map(|(x, y)| (x - 1, y))
| ^^^^^^ expected &(i32, i32), found tuple
|
= note: expected type `&(i32, i32)`
found type `(_, _)`
答案 0 :(得分:3)
我不太明白。类型
&mut (i32, i32)
不是元组 本身?
不,它是对元组的可变引用。
在迭代它时,你也无法从矢量中删除元素,因为这可能会导致悬空指针。 remove
还会删除索引处的元素。你必须以不同的方式做事:
type some_t = i32;
struct SomeStruct {
pub some_tuple_vector: Vec<(some_t, some_t)>,
}
impl SomeStruct {
fn some_method(&mut self) {
self.some_tuple_vector.retain(|tuple| tuple.0 >= 0);
for tuple in &mut self.some_tuple_vector {
tuple.1 -= 1;
}
}
}
fn main() {}
这也可以在loop
中一次完成,但这可能已经足够了。
答案 1 :(得分:2)
Vec::iter_mut
返回实现Iterator<&'a mut T>
的{{3}}。也就是说,它是一系列指向载体内存的可变引用。这些是指针(如C / C ++)而不是值本身。
如果要处理这些内存位置(读取或写入)的数据,则需要取消引用。当使用.
运算符调用方法或访问字段时会自动发生这种情况,但在模式匹配或分配时不会发生这种情况。也就是说,p = ...
正在尝试将局部变量p
设置为新值,而不是将数据分配给它指向的内存。后者是通过*p = ...
完成的。
因此,具有更好工作机会的重写是:
for p in self.some_tuple_vector.iter_mut() {
match *p {
(x, y) if x < 0 => self.some_tuple_vector.remove(p),
(x, y) => *p = (x, y - 1),
}
}
但是,由于remove
采用的是索引而不是值,因此仍然存在错误。最好通过调用remove
并分别循环向量来递减retain
,MutItems
来完成y
。
您的替代解决方案只需要模式匹配。通过引用进行模式匹配是使用&
:解构镜构建。
self.some_tuple_vector = self.some_tuple_vector
.iter()
.map(|&(x, y)| (x - 1, y))
.filter(|&(x, y)| x > 0);
然而,这导致了这个相当长的错误消息:
= note: expected type `std::vec::Vec<(i32, i32)>`
found type `std::iter::Filter<std::iter::Map<std::slice::Iter<'_, (i32, i32)>, [closure@src/main.rs:11:18: 11:38]>, [closure@src/main.rs:12:21: 12:36]>`
问题是像map
和filter
这样的迭代器适配器返回延迟迭代器对象,它们并不急切地评估结果的Vec
。要将序列的值收集到具体类型Vec
中,您只需要调用as A.B. suggests方法:
self.some_tuple_vector =
self.some_tuple_vector.iter()
.map(|&(x,y)| (x-1,y))
.filter(|&(x,y)| x > 0)
.collect();
collect
是通用的,适用于许多集合类型(任何实现collect
的东西),但在这种情况下,编译器可以推断出所需类型为Vec<(some_t, some_t)>
,因为它被分配到那个类型的领域。
然而,这是分配一个全新的向量而只是丢弃旧的向量,因此可能比retain
+ iter_mut
解决方案慢,除非大多数元素将被删除过滤。
答案 2 :(得分:0)
这应该有效:
type some_t = i32;
struct SomeStruct {
pub some_tuple_vector: Vec<(some_t,some_t)>
}
impl SomeStruct {
fn some_method(&mut self) {
for p in self.some_tuple_vector.iter_mut() {
match *p {
(x,y) if x < 0 => self.some_tuple_vector.remove(p),
(x,y) => p = (x, y-1)
}
}
}
}
虽然Rust在访问成员方法或数据时会执行自动反射引用,但在检查相等/匹配时它不会进行自动反射,您必须明确使用*
运算符。
&mut (a,b)
不是元组,它是对元组的引用。
答案 3 :(得分:0)
RFC 2005改善了参考文献匹配的人体工程学。这可以使(x, y)
能够与&mut (T, T)
进行模式匹配,从而帮助您立即发生错误。
此外,最近添加的Vec::drain_filter
方法可以完全满足您所需的Vec
转换。
不幸的是,这两个都是不稳定的功能:
#![feature(drain_filter)]
#![feature(match_default_bindings)]
#[derive(Debug)]
struct SomeStruct {
some_tuple_vector: Vec<(i32, i32)>,
}
impl SomeStruct {
fn some_method(&mut self) {
self.some_tuple_vector.drain_filter(|(x, y)| {
if *x < 0 {
true // remove
} else {
*y -= 1;
false // keep
}
});
}
}
fn main() {
let mut ss = SomeStruct {
some_tuple_vector: vec![(-1, -1), (0, 0), (1, 1)],
};
ss.some_method();
println!("{:?}", ss);
}
打印:
SomeStruct { some_tuple_vector: [(0, -1), (1, 0)] }