为类型参数传递 Rust 生命周期

Communicating Rust Lifetimes for Type Parameters

我正在处理一个简单的复数示例,并尝试按如下方式实现 ref-value/value-ref 操作:

use std::ops::*;

#[derive(Clone, PartialEq)]
pub struct Complex<T: Sized + Clone> {
    pub re: T,
    pub im: T,
}

// Ref-Ref Multiplication
impl<'a, 'b, T: Sized + Clone> Mul<&'b Complex<T>> for &'a Complex<T>
where
    T: Add<T, Output = T>,
    T: Sub<T, Output = T>,
    &'a T: Add<&'b T, Output = T>,
    &'a T: Mul<&'b T, Output = T>,
    &'a T: Sub<&'b T, Output = T>,
{
    type Output = Complex<T>;
    fn mul(self, rhs: &'b Complex<T>) -> Complex<T> {
        panic!("// Details irrelevant")
    }
}

// Ref-Value Multiplication
impl<'a, 'b, T: Sized + Clone> Mul<Complex<T>> for &'a Complex<T>
where
    T: 'static,
    T: Add<T, Output = T>,
    T: Sub<T, Output = T>,
    &'a T: Add<&'b T, Output = T>,
    &'a T: Mul<&'b T, Output = T>,
    &'a T: Sub<&'b T, Output = T>,
{
    type Output = Complex<T>;
    fn mul(self, rhs: Complex<T>) -> Complex<T> {
        let t = &rhs;
        self.mul(t)
    }
}

ref-ref 实现有效,据我了解,它接受两个不同生命周期的引用,returns 一个复杂的值类型。参考值部分是我遇到问题的地方;当我编译时,错误是 rhs 的寿命不够长。我相信我已经知道为什么会这样了,那就是 T 可以在返回值时持有对 rhs 的引用(直接或间接),因此 rhs 超出范围,但 T 可以仍然保留对它的引用。

我的问题是如何传达 T 不会以某种形式或形式引用 rhs

关于我迄今为止尝试过或看过的事情的一些注释:

  1. 更改了任一 Mul 实现的生命周期规范。
  2. 尝试了生命周期继承,但这指定了 T 持有的引用将至少与 T 一样长,所以我想我需要在 [=34= 行中添加更多内容]
  3. 查看了其他实现;要么没有实现案例,要么只是使用克隆来绕过问题。

正如 Peter Hall 在评论中所建议的那样,最简单的解决方案是为您的复杂类型派生 Copy,并实现值的操作。对于 ref-ref 实现和 ref-val 实现,您可以简单地取消引用引用并使用 val-val 实现。

如果你想让你开始的方法起作用,你需要更高等级的特征边界:

use std::ops::*;

#[derive(Clone, PartialEq)]
pub struct Complex<T: Clone> {
    pub re: T,
    pub im: T,
}

// Ref-Ref Multiplication
impl<'a, 'b, T: Clone> Mul<&'b Complex<T>> for &'a Complex<T>
where
    T: Add<T, Output = T>,
    T: Sub<T, Output = T>,
    &'a T: Add<&'b T, Output = T>,
    &'a T: Mul<&'b T, Output = T>,
    &'a T: Sub<&'b T, Output = T>,
{
    type Output = Complex<T>;
    fn mul(self, rhs: &'b Complex<T>) -> Complex<T> {
        Complex {
            re: &self.re * &rhs.re - &self.im * &rhs.im,
            im: &self.re * &rhs.im + &self.im * &rhs.re,
        }
    }
}

// Ref-Value Multiplication
impl<'a, T: Clone> Mul<Complex<T>> for &'a Complex<T>
where
    T: Add<T, Output = T>,
    T: Sub<T, Output = T>,
    &'a T: for<'b> Add<&'b T, Output = T>,
    &'a T: for<'b> Mul<&'b T, Output = T>,
    &'a T: for<'b> Sub<&'b T, Output = T>,
{
    type Output = Complex<T>;
    fn mul(self, rhs: Complex<T>) -> Complex<T> {
        let t = &rhs;
        self.mul(t)
    }
}

在您的版本中,ref-value 实现中的生命周期 'b 由特征的用户选择。由于用户可以使用 'b 的任何生命周期,因此 rhs 需要静态生命周期才能使您的代码有效。相反,您想要的是 *'a T 满足 any 给定生命周期 'b 的给定特征界限,这正是 HRTB 的用途。

为第二个实现编写特征边界的另一种重复性较低的方法是:

impl<'a, T: Clone> Mul<Complex<T>> for &'a Complex<T>
where
    Self: for<'b> Mul<&'b Complex<T>, Output = Complex<T>>,
{
    type Output = Complex<T>;
    fn mul(self, rhs: Complex<T>) -> Complex<T> {
        self.mul(&rhs)
    }
}

内置数字类型使用 macro 实现这些排列。手工完成,我将从你乘以两个值而不是任何引用的情况开始,并确保你的 Complex 结构是 Copy:

impl<T: Copy> Mul<Complex<T>> for Complex<T>
where
    T: Add<T, Output = T>,
    T: Sub<T, Output = T>,
    T: Mul<T, Output = T>,
{
    type Output = Complex<T>;
    fn mul(self, rhs: Complex<T>) -> Complex<T> {
        unimplemented!()
    }
}

请注意,您不需要对 &T 进行任何限制 - 您无论如何都不能 return 引用 T,因此您将不得不复制它们,这这就是我指定 T: Copy.

的原因

其余的实现现在很简单,可以委托给最简单的情况:

impl<'a, T: Copy> Mul<Complex<T>> for &'a Complex<T>
where
    T: Add<T, Output = T>,
    T: Sub<T, Output = T>,
    T: Mul<T, Output = T>,
{
    type Output = Complex<T>;
    fn mul(self, rhs: Complex<T>) -> Complex<T> {
        (*self).mul(rhs)
    }
}

impl<'a, T: Copy> Mul<&'a Complex<T>> for Complex<T>
where
    T: Add<T, Output = T>,
    T: Sub<T, Output = T>,
    T: Mul<T, Output = T>,
{
    type Output = Complex<T>;
    fn mul(self, rhs: &'a Complex<T>) -> Complex<T> {
        self.mul(*rhs)
    }
}

impl<'a, 'b, T: Copy> Mul<&'a Complex<T>> for &'b Complex<T>
where
    T: Add<T, Output = T>,
    T: Sub<T, Output = T>,
    T: Mul<T, Output = T>,
{
    type Output = Complex<T>;
    fn mul(self, rhs: &'a Complex<T>) -> Complex<T> {
        (*self).mul(*rhs)
    }
}