如何在实现特征时对类型施加特征约束?
How can I put a trait constraint on a type while implementing a trait?
我有一个生成斐波那契数列的迭代器。我将类型限制为 u32
,但现在我正在努力使其对任何数字类型都通用。
有效的非通用代码:
struct Fib {
value: u32,
next: u32,
}
impl Fib {
fn new( a : u32, b : u32 ) -> Fib {
Fib { value : a, next : b }
}
}
impl Iterator for Fib {
type Item = u32;
fn next(&mut self) -> Option<u32> {
let value = self.value;
let next = self.value + self.next;
self.value = self.next;
self.next = next;
Some( value )
}
}
//////////////////////////////////////////////////
fn main() {
let fib = Fib::new( 1, 2 );
let sum = fib.filter( |x| { x % 2 == 0 })
.take_while( |&x| { x <= 4000000 })
.fold( 0, |sum, x| { sum + x });
println!("{}", sum);
}
问题是 Iterator
的实现需要对 Num
的约束,但我不知道如何表达:
impl <T : Num> Iterator for Fib<T> { ... }
生产:
use of undeclared trait name `Num`
当我尝试 use std::num::{Num}
或 use num::traits::{Num}
时,我被告知模块不存在。
我认为您不希望 Fib
泛化于数字类型,而是实现 +
运算符的类型。像这样:
use std::ops::Add;
struct Fib<N>
where N: Add<Output = N> + Copy {
value: N,
next: N,
}
impl<N> Iterator for Fib<N>
where N: Add<Output = N> + Copy {
type Item = N;
fn next(&mut self) -> Option<N> {
let next = self.value + self.next;
self.value = self.next;
self.next = next;
Some(next)
}
}
fn main() {
let fib_seq = Fib {
value: -1,
next: 1,
};
for thing in fib_seq.take(10) {
println!("{}", thing);
}
}
Add
是允许您使用 +
运算符并生成 Output
的特征。在这种情况下,N
实现了 Add<Output = N>
特征,这意味着 N + N
将产生 N
.
类型的东西
听起来是这样,但是当您尝试执行 self.next + self.value
时,您正在 移动 value
和 next
从 self
导致错误。
你不能不移动这些值,因为 add 的定义有这个方法签名:
fn add(self, rhs: RHS) -> Self::Output;
RHS
在 Add
的情况下只是 Self
。因此,为了将 N
限制为只需很少开销即可复制的类型,我添加了 Copy
特征作为限制。
OP 提到了一个有趣的观点:Is it possible to alias traits?简而言之,没有。
您可以创造一个新特征:
trait SimpleAdd: Add<Output = Self> + Copy {
}
但是你必须为你想要的所有类型实现那个特征。 IE。 i32
不会自动实现 SimpleAdd
。但是如果你愿意,你可以用泛型来做到这一点:
impl<N> SimpleAdd for N
where N: Add<Output = N> + Copy {
}
所以上面的两个块会让你得到与特征别名相同的东西,但这看起来很麻烦。
我有一个生成斐波那契数列的迭代器。我将类型限制为 u32
,但现在我正在努力使其对任何数字类型都通用。
有效的非通用代码:
struct Fib {
value: u32,
next: u32,
}
impl Fib {
fn new( a : u32, b : u32 ) -> Fib {
Fib { value : a, next : b }
}
}
impl Iterator for Fib {
type Item = u32;
fn next(&mut self) -> Option<u32> {
let value = self.value;
let next = self.value + self.next;
self.value = self.next;
self.next = next;
Some( value )
}
}
//////////////////////////////////////////////////
fn main() {
let fib = Fib::new( 1, 2 );
let sum = fib.filter( |x| { x % 2 == 0 })
.take_while( |&x| { x <= 4000000 })
.fold( 0, |sum, x| { sum + x });
println!("{}", sum);
}
问题是 Iterator
的实现需要对 Num
的约束,但我不知道如何表达:
impl <T : Num> Iterator for Fib<T> { ... }
生产:
use of undeclared trait name `Num`
当我尝试 use std::num::{Num}
或 use num::traits::{Num}
时,我被告知模块不存在。
我认为您不希望 Fib
泛化于数字类型,而是实现 +
运算符的类型。像这样:
use std::ops::Add;
struct Fib<N>
where N: Add<Output = N> + Copy {
value: N,
next: N,
}
impl<N> Iterator for Fib<N>
where N: Add<Output = N> + Copy {
type Item = N;
fn next(&mut self) -> Option<N> {
let next = self.value + self.next;
self.value = self.next;
self.next = next;
Some(next)
}
}
fn main() {
let fib_seq = Fib {
value: -1,
next: 1,
};
for thing in fib_seq.take(10) {
println!("{}", thing);
}
}
Add
是允许您使用 +
运算符并生成 Output
的特征。在这种情况下,N
实现了 Add<Output = N>
特征,这意味着 N + N
将产生 N
.
听起来是这样,但是当您尝试执行 self.next + self.value
时,您正在 移动 value
和 next
从 self
导致错误。
你不能不移动这些值,因为 add 的定义有这个方法签名:
fn add(self, rhs: RHS) -> Self::Output;
RHS
在 Add
的情况下只是 Self
。因此,为了将 N
限制为只需很少开销即可复制的类型,我添加了 Copy
特征作为限制。
OP 提到了一个有趣的观点:Is it possible to alias traits?简而言之,没有。 您可以创造一个新特征:
trait SimpleAdd: Add<Output = Self> + Copy {
}
但是你必须为你想要的所有类型实现那个特征。 IE。 i32
不会自动实现 SimpleAdd
。但是如果你愿意,你可以用泛型来做到这一点:
impl<N> SimpleAdd for N
where N: Add<Output = N> + Copy {
}
所以上面的两个块会让你得到与特征别名相同的东西,但这看起来很麻烦。