[英]How to write a trait bound for adding two references of a generic type?
I have a Fibonacci
struct that can be used as an iterator for anything that implements One
, Zero
, Add
and Clone
. 我有一个
Fibonacci
结构,可以用作实现One
, Zero
, Add
和Clone
任何东西的迭代器。 This works great for all integer types. 这适用于所有整数类型。
I want to use this struct for BigInteger
types which are implemented with a Vec
and are expensive to call clone()
on. 我想将此结构用于使用
Vec
实现的BigInteger
类型,并且调用clone()
的Vec
很昂贵。 I would like to use Add
on two references to T
which then returns a new T
(no cloning then). 我想在两个对
T
引用上使用Add
,然后返回一个新的T
(然后没有克隆)。
For the life of me I can't make one that compiles though... 对于我的生活,我不能制作一个虽然编译的...
Working: 工作:
extern crate num;
use std::ops::Add;
use std::mem;
use num::traits::{One, Zero};
pub struct Fibonacci<T> {
curr: T,
next: T,
}
pub fn new<T: One + Zero>() -> Fibonacci<T> {
Fibonacci {
curr: T::zero(),
next: T::one(),
}
}
impl<'a, T: Clone + Add<T, Output = T>> Iterator for Fibonacci<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
mem::swap(&mut self.next, &mut self.curr);
self.next = self.next.clone() + self.curr.clone();
Some(self.curr.clone())
}
}
#[test]
fn test_fibonacci() {
let first_12 = new::<i64>().take(12).collect::<Vec<_>>();
assert_eq!(vec![1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144], first_12);
}
Desired: 期望:
extern crate num;
use std::ops::Add;
use std::mem;
use num::traits::{One, Zero};
pub struct Fibonacci<T> {
curr: T,
next: T,
}
pub fn new<T: One + Zero>() -> Fibonacci<T> {
Fibonacci {
curr: T::zero(),
next: T::one(),
}
}
impl<'a, T: Clone + 'a> Iterator for Fibonacci<T>
where
&'a T: Add<&'a T, Output = T>,
{
type Item = T;
fn next(&mut self) -> Option<T> {
mem::swap(&mut self.next, &mut self.curr);
self.next = &self.next + &self.curr;
Some(self.curr.clone())
}
}
#[test]
fn test_fibonacci() {
let first_12 = new::<i64>().take(12).collect::<Vec<_>>();
assert_eq!(vec![1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144], first_12);
}
This gives the error 这给出了错误
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/main.rs:27:21
|
27 | self.next = &self.next + &self.curr;
| ^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 25:5...
--> src/main.rs:25:5
|
25 | / fn next(&mut self) -> Option<T> {
26 | | mem::swap(&mut self.next, &mut self.curr);
27 | | self.next = &self.next + &self.curr;
28 | | Some(self.curr.clone())
29 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:27:21
|
27 | self.next = &self.next + &self.curr;
| ^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 19:1...
--> src/main.rs:19:1
|
19 | / impl<'a, T: Clone + 'a> Iterator for Fibonacci<T>
20 | | where
21 | | &'a T: Add<&'a T, Output = T>,
22 | | {
... |
29 | | }
30 | | }
| |_^
note: ...so that types are compatible (expected std::ops::Add, found std::ops::Add<&'a T>)
--> src/main.rs:27:32
|
27 | self.next = &self.next + &self.curr;
| ^
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/main.rs:27:34
|
27 | self.next = &self.next + &self.curr;
| ^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 25:5...
--> src/main.rs:25:5
|
25 | / fn next(&mut self) -> Option<T> {
26 | | mem::swap(&mut self.next, &mut self.curr);
27 | | self.next = &self.next + &self.curr;
28 | | Some(self.curr.clone())
29 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:27:34
|
27 | self.next = &self.next + &self.curr;
| ^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 19:1...
--> src/main.rs:19:1
|
19 | / impl<'a, T: Clone + 'a> Iterator for Fibonacci<T>
20 | | where
21 | | &'a T: Add<&'a T, Output = T>,
22 | | {
... |
29 | | }
30 | | }
| |_^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:27:34
|
27 | self.next = &self.next + &self.curr;
| ^^^^^^^^^^
How to write a trait bound for adding two references of a generic type?
如何写一个特征绑定添加两个泛型类型的引用?
Let's start with a simplified example: 让我们从一个简化的例子开始:
fn add_things<T>(a: &T, b: &T) {
a + b;
}
This has the error 这有错误
error[E0369]: binary operation `+` cannot be applied to type `&T`
--> src/lib.rs:2:5
|
2 | a + b;
| ^^^^^
|
= note: an implementation of `std::ops::Add` might be missing for `&T`
As the compiler hints, we need to guarantee that Add
is implemented for &T
. 正如编译器提示的那样,我们需要保证为
&T
实现Add
。 We can express that directly by adding an explicit lifetime to our types and also using that in our trait bounds: 我们可以通过向我们的类型添加显式生命周期并在我们的特征边界中使用它来直接表达:
use std::ops::Add;
fn add_things<'a, T>(a: &'a T, b: &'a T)
where
&'a T: Add,
{
a + b;
}
Next, let's try a slightly different approach — instead of being handed a reference, we will create one inside the function: 接下来,让我们尝试一种稍微不同的方法 - 我们将在函数内部创建一个,而不是提交引用。
fn add_things<T>(a: T, b: T) {
let a_ref = &a;
let b_ref = &b;
a_ref + b_ref;
}
We get the same error: 我们得到了同样的错误:
error[E0369]: binary operation `+` cannot be applied to type `&T`
--> src/lib.rs:5:5
|
5 | a_ref + b_ref;
| ^^^^^^^^^^^^^
|
= note: an implementation of `std::ops::Add` might be missing for `&T`
However, trying to add the same fix as before doesn't work. 但是,尝试添加与以前相同的修复程序不起作用。 It's also a bit awkward because the lifetime isn't associated with any of the arguments passed in:
它也有点尴尬,因为生命周期与传入的任何参数无关:
use std::ops::Add;
fn add_things<'a, T: 'a>(a: T, b: T)
where
&'a T: Add,
{
let a_ref = &a;
let b_ref = &b;
a_ref + b_ref;
}
error[E0597]: `a` does not live long enough
--> src/lib.rs:7:17
|
3 | fn add_things<'a, T: 'a>(a: T, b: T)
| -- lifetime `'a` defined here
...
7 | let a_ref = &a;
| ^^
| |
| borrowed value does not live long enough
| assignment requires that `a` is borrowed for `'a`
...
11 | }
| - `a` dropped here while still borrowed
Placing the 'a
lifetime on the impl
means that the caller of the method gets to determine what the lifetime should be. 将
'a
生命周期放在impl
意味着方法的调用者可以确定生命周期应该是什么。 Since the reference is taken inside the method, the caller can never even see what that lifetime would be. 由于引用是在方法内部进行的,因此调用者甚至无法看到生命周期是什么。
Instead, you want to place a restriction that a reference of an arbitrary lifetime implements a trait. 相反,您希望限制任意生命周期的引用实现特征。 This is called a Higher Ranked Trait Bound (HRTB):
这被称为更高级别的特质界限 (HRTB):
use std::ops::Add;
fn add_things<T>(a: T, b: T)
where
for<'a> &'a T: Add,
{
let a_ref = &a;
let b_ref = &b;
a_ref + b_ref;
}
Applied back to your original code, you were very close: 应用回原始代码,您非常接近:
impl<T> Iterator for Fibonacci<T>
where
T: Clone,
for<'a> &'a T: Add<Output = T>,
{
type Item = T;
fn next(&mut self) -> Option<T> {
mem::swap(&mut self.next, &mut self.curr);
self.next = &self.next + &self.curr;
Some(self.curr.clone())
}
}
See also: 也可以看看:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.