你可以在没有显式引用或所有权移动的情况下在结构上实现数学操作吗?

时间:2018-03-01 22:51:51

标签: rust borrow-checker borrowing

我无法弄清楚如何在结构上拥有干净的数学,而不需要在任何地方复制这些结构值。

如果你想要一个可以进行数学运算的结构,你可以这样写:

use std::ops::*;

struct Num {
    i: i32,
}

impl Add for Num {
    type Output = Num;
    fn add(self, other: Num) -> Num {
        Num {
            i: self.i + other.i,
        }
    }
}

(这是一个简化的例子。一个实际的例子可能是做矢量数学)

这让我们可以编写漂亮的a + (b / (c * d))样式代码。

由于借用语义,上述代码与a + b + a一样快。一旦a被使用,它就无法再次使用,因为所有权已转移到相关功能(即add)。

解决这个问题的简单方法是为结构实现Copy

#[derive(Copy)]
struct Num {
    i: i32,
}

这意味着当Num传递给add时,会自动克隆它们的值,以便可以干净地删除它们。

但这似乎效率低下!我们不需要在整个地方复制这些结构:它是只读的,我们真的只需要引用它来创建我们返回的新结构。

这让我认为我们应该在引用上实现数学运算:

impl<'a> Add for &'a Num {
    type Output = Num;
    fn add(&'a self, other: &'a Num) -> Num {
        Num {
            i: self.i + other.i,
        }
    }
}

现在我们有数学运算,我们不会在所有地方克隆数据,但现在我们的数学看起来很糟糕! a + (b / (c * d))现在必须是&a + &(&b / &(&c * &d))。如果你有值类型引用(例如let a = &Num { /* ... */ }),它也没有帮助,因为add的返回值仍然是Num

是否有一种干净的方法来实现结构的操作,使数学运算看起来干净,并且结构值不会被复制到任何地方?

相关:

1 个答案:

答案 0 :(得分:3)

没有;这些特征是按价值消费的,没有办法解决这个问题。

  

但这似乎效率低下!我们不需要在整个地方复制这些结构:它是只读的,我们真的只需要引用它来创建我们返回的新结构。

我不担心复制单个整数的效率。这就是计算机做的事情。实际上,引用可能会更慢,因为引用也基本上是一个必须被复制的整数,然后必须查找一块内存,将引用的整数复制到寄存器中

  

我很明显[不]复制一个整数!

  

一个实际的例子可能是做矢量数学

然后问题变得混淆了您的用户。在不查看实现的情况下,用户如何知道a + a是否“轻量级”。如果你,你的类型的实现者知道它是轻量级的,你可以标记它Copy。如果不是,则需要进行参考。

这就是今天的情况。有一些experimental work可能确实会在未来使这个更好一点:

  

想象永远不必再次写[...] let z = &u * &(&(&u.square() + &(&A * &u)) + &one);

此实验源于a now-deferred RFC

除了有趣之外,这种丑陋的语法被称为Eye of Sauron