< 返回版块

ishiguang 发表于 2021-06-29 16:25

Tags:范型,关联类型

1.声明了一个范型结构体:

struct Complex<T> {
    re: T,
    im: T
}

2.为结构体实现+操作符操作,即实现 Add trait

impl<T: Add<T, Output=T>> Add for Complex<T> {
    type Output = Complex<T>;

    fn add(self, rhs: Complex<T>) -> Self::Output {
        Complex { re: self.re + rhs.re, im: self.im + rhs.im }
    }
}

3.问题就出现在这个 T: Add<T, Output=T>,系统的 Add trait的定义为 pub trait Add<Rhs = Self> {...} 明明范型参数只有一个 Rhs,并且默认值是Self,为什么那段代码可以写成 Add<T, Output=T>,这里明明有 2 个范型参数呢,一个T,一个Output,有点看不明白,另外Output=T应该对Output设置默认值吧,但是方法定义里面又定义了type Output = Complex<T>,感觉好绕。

评论区

写评论
Pikachu 2021-06-30 06:43

最后放一下可执行的rust playground代码,是我上面回复里面用到的例子。

rust playground

Pikachu 2021-06-30 06:35

问题大体可以分成两个部分,第一是对于Add trait的理解,第二是对于impl<T>的理解。


首先,Add trait是这么定义的。

pub trait Add<Rhs = Self> {
    type Output;
    #[must_use]
    pub fn add(self, rhs: Rhs) -> Self::Output;
}

所以在形如a+b=c的式子中,涉及了三种类型:加号左边aSelf类型,加号右边bRhs类型,最终的结果cOutput类型。这三种类型之间没有任何联系(尽管在默认情况下,Rhs和Self是同一种类型,但它们可以是不同的)。所以我们可以实现下面这种这种例子。其中Output类型被称为associate type,因为当加号左右两边的类型已知时,结果的类型也应当唯一确定。关于associate type的更多内容,请阅读The book 19.2

use std::ops::Add;

struct A(pub u32);
struct B(pub u32);
struct C(pub u32);

impl Add<B> for A {
    type Output = C;

    fn add(self, rhs: B) -> Self::Output {
        let A(a) = self;
        let B(b) = rhs;
        C(a + b)
    }
}

然后是关于impl<T>的理解。我把Complex的例子修改如下(和原问题中的代码不是一回事了)。用where来表明trait bound会更清晰一点。

impl<T, U> Add<Complex<U>> for Complex<T>
where
    T: Add<U>,
{
    type Output = Complex<<T as Add<U>>::Output>;

    fn add(self, rhs: Complex<U>) -> Self::Output {
        Self::Output {
            re: self.re + rhs.re,
            im: self.im + rhs.im,
        }
    }
}

上面的前三行代码读作:

“如果TU之间可以做加法运算,那么实现Complex<T>Complex<U>之间的加法。”

上面的第五行代码读作:

“把T+U的结果类型记作K,则Complex<T>+Complex<U>的结果类型是Complex<K>。”


最后我们回到原本的问题上来,题目中的代码读作:

“如果TT之间可以做加法运算,而且结果的类型也是T,那么实现Complex<T>Complex<T>之间的加法,其结果类型是Complex<T>。”

作者 ishiguang 2021-06-29 17:13

看了一本书,精通rust(第二版),里面的事例代码。

--
👇
ges-AS: Add里面的Output是关联类型,必须要指定,因为有计算结果与左右两个数据类型不一样的可能(比如-1开方) T的Add和Complex的Add要分开来看,impl里面都是属于描述Complex的,外面是描述T的 (ps:话说为什么要搞一个范型,直接f64不行吗)

ges-AS 2021-06-29 16:59

Add里面的Output是关联类型,必须要指定,因为有计算结果与左右两个数据类型不一样的可能(比如-1开方) T的Add和Complex的Add要分开来看,impl里面都是属于描述Complex的,外面是描述T的 (ps:话说为什么要搞一个范型,直接f64不行吗)

Grobycn 2021-06-29 16:53

Output 是 associated type

1 共 5 条评论, 1 页