为什么在尝试匹配元组时会出现不匹配的类型错误?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么在尝试匹配元组时会出现不匹配的类型错误?相关的知识,希望对你有一定的参考价值。
这是我期望工作的一些不言自明的代码:
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 `(_, _)`
我不太明白。
&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
一次通过,但这可能是足够好的。
Vec::iter_mut
返回一个实施MutItems
的Iterator<&'a mut T>
。也就是说,它是一系列指向载体内存的可变引用。这些是指针(如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
并分别在矢量上循环来减少y
,as A.B. suggests。
您的替代解决方案只需要模式匹配。通过引用进行模式匹配是通过&
:destructuring mirror construct来完成的。
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
s。要将序列的值收集到像Vec
这样的具体类型中,您只需要调用collect
方法:
self.some_tuple_vector =
self.some_tuple_vector.iter()
.map(|&(x,y)| (x-1,y))
.filter(|&(x,y)| x > 0)
.collect();
collect
是通用的,适用于许多集合类型(任何实现FromIterator
的东西),但在这种情况下,编译器可以推断出所需的类型是Vec<(some_t, some_t)>
,因为它被分配给具有该类型的字段。
然而,这是分配一个全新的向量而只是丢弃旧的向量,因此可能比retain
+ iter_mut
解决方案慢,除非过滤将删除大部分元素。
这应该工作:
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)
不是元组,它是对元组的引用。
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)] }
以上是关于为什么在尝试匹配元组时会出现不匹配的类型错误?的主要内容,如果未能解决你的问题,请参考以下文章
使用3个元素元组时为什么会出现CombineByKey错误?
为啥numpy在尝试预测回归时会引发异常错误:“ufunc'add'没有包含具有签名匹配类型的循环”?