为什么在尝试匹配元组时会出现不匹配的类型错误?

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返回一个实施MutItemsIterator<&'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并分别在矢量上循环来减少yas 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]>`

问题是像mapfilter这样的迭代器适配器返回惰性迭代器对象,它们并没有急切地评估结果的Vecs。要将序列的值收集到像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)] }

以上是关于为什么在尝试匹配元组时会出现不匹配的类型错误?的主要内容,如果未能解决你的问题,请参考以下文章

Ocaml模式匹配“方形”元组?

使用3个元素元组时为什么会出现CombineByKey错误?

为啥numpy在尝试预测回归时会引发异常错误:“ufunc'add'没有包含具有签名匹配类型的循环”?

Scala Spark 地图类型匹配问题

使用 updateOptions() 更新 FirestoreRecyclerAdapter 会出现类型不匹配错误

使用 updateOptions() 更新 FirestoreRecyclerAdapter 会出现类型不匹配错误