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

时间:2014-10-18 10:22:33

标签: pattern-matching tuples rust

这是我希望工作的一些不言自明的代码:

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 `(_, _)`

4 个答案:

答案 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并分别循环向量来递减retainMutItems来完成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]>`

问题是像mapfilter这样的迭代器适配器返回延迟迭代器对象,它们并不急切地评估结果的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)] }