我正在研究一个简单的复数示例,并尝试实现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实现有效,据我了解,它接受两个具有不同生命周期的引用,并返回一个复杂的值类型。 ref-value部分是我遇到问题的地方;当我编译时,错误是rhs
的寿命不足。我相信我知道为什么会这样,就是说T可以在返回值时持有对rhs的引用(直接或间接),因此rhs
超出范围,但是T
可以保持对它的引用。
我的问题是如何传达T
不会以某种形状或形式保留对rhs
的引用。
关于到目前为止我已经尝试过或研究过的事情的一些注释:
T
持有的引用的生存期至少与T
一样长,因此我认为“最多”还需要更多内容。 / li>
答案 0 :(得分:1)
正如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
满足任何给定生命周期'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)
}
}
答案 1 :(得分:0)
内置数字类型使用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
不需要任何约束-无论如何您都无法返回对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)
}
}