与 Rust 勾心斗角 · 快没有爱了

garfileo山东
English

基于 Rust 的 Vec 容器,可以表达数学里的 n 维点。下面的 foo 函数可以根据两个 n 维点 a 和 b,构造一个点集 {a, b, c},其中 c 是 a 和 b 的中点。

fn foo(a: &Vec<f64>, b: &Vec<f64>) -> Vec<Vec<f64>> {
    assert_eq!(a.len(), b.len());
    let mut c: Vec<f64> = Vec::new();
    for i in 0 .. a.len() {
        c.push(0.5 * (a[i] + b[i]));
    }
    let mut points: Vec<Vec<f64>> = Vec::new();
    points.push(a.clone());
    points.push(b.clone());
    points.push(c);
    return points;
}

注意,points 存储的是 ab 的副本,并非 ab 本身。倘若我希望 points 里存储的是 ab 本身,该如何做呢?我能想到的方案是

fn foo(a: &Vec<f64>, b: &Vec<f64>) -> Vec<&Vec<f64>> {
    assert_eq!(a.len(), b.len());
    let mut c: Vec<f64> = Vec::new();
    for i in 0 .. a.len() {
        c.push(0.5 * (a[i] + b[i]));
    }
    let mut points: Vec<&Vec<f64>> = Vec::new();
    points.push(a);
    points.push(b);
    points.push(&c);
    return points;
}

现在,points 存储的是 abc 的引用,但是 rustc 在编译这段代码时,会报错并指出,这个函数的返回值包含了一个借用值,但是函数签名却未能指明借用的是 a 还是 b。虽然代码写的很明白,ab 都被借用了,但即便如此,rustc 依然无法确定在 points 生命周期之内 ab 是否仍然健在,因此需要通过生命周期标记告诉 rustc,a 以及 b 的生命周期至少与 points 一样长,亦即

fn foo<'a>(a: &'a Vec<f64>, b: &'a Vec<f64>) -> Vec<&'a Vec<f64>> {
    ... ... ...
}

真正致命的问题在于 c,即

points.push(&c);

rustc 在编译这行代码时,会报错:

error[E0515]: cannot return value referencing local variable `c`

意思是,不能将一个局部变量的引用放在 points 里并将其返回。因为 foo 函数结束时,c 便寿终正寢,从而导致 points 对它的引用失效。我现在无法解决这个问题,只好乖乖地回到了最初的方案,用 pointsab 所引用的点的副本,再存 c

现在,再来写一个函数 bar,用它修改 points,将 c 放在 ab 的中间:

fn bar(points: &mut Vec<Vec<f64>>) {
    let b = points[1];
    points[1] = points[2];
    points[2] = b;
}

逻辑上没问题,但是 rustc 认为,

move occurs because value has type `Vec<f64>`, which does not implement the `Copy` trait

意思就是 bar 里面的三条赋值语句,都是错的,原因是,Vec<f64> 没实现 Copy Trait。要 Copy 什么呢?我只是希望 points 的第 2 个和第 3 个元素交换一下位置,并不想 Copy 什么啊。然而,我却不知道该怎样写这个函数了,倘若 points 存储的是引用,而不是实际的值,上述赋值语句应该是成立的,但是这又会导致 foo 函数难以定义。

感觉对 rust 快没有爱了……好在一开始就没有。

最后,上述问题是这样解决的:

fn foo(a: &Vec<f64>, b: &Vec<f64>) -> Vec<Vec<f64>> {
    assert_eq!(a.len(), b.len());
    let mut c: Vec<f64> = Vec::new();
    for i in 0 .. a.len() {
        c.push(0.5 * (a[i] + b[i]));
    }
    let mut points: Vec<Vec<f64>> = Vec::new();
    points.push(a.clone());
    points.push(b.clone());
    points.push(c);
    return points;
}

fn bar(points: &Vec<Vec<f64>>) -> Vec<&Vec<f64>> {
    let mut points_ref: Vec<&Vec<f64>> = Vec::new();
    for x in points {
        points_ref.push(x);
    }
    let b = points_ref[1];
    points_ref[1] = points_ref[2];
    points_ref[2] = b;
    return points_ref;
}
阅读 379

5.9k 声望
1.9k 粉丝
0 条评论
5.9k 声望
1.9k 粉丝
文章目录
宣传栏