简体   繁体   English

如何写一个特征绑定添加两个泛型类型的引用?

[英]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结构,可以用作实现OneZeroAddClone任何东西的迭代器。 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.

相关问题 如何在特征本身上写一个特征绑定来引用关联类型? - How to write a trait bound for a reference to an associated type on the trait itself? 如何在另一个泛型类型的特征绑定的类型参数上表达特征? - How can I express a trait bound on a type parameter for another generic type's trait bound? 如何将特征绑定到非泛型类型? - How to add trait bound to a non-generic type? 性状受制于普通特征 - Trait bound on generic trait 如果在函数内创建引用,如何将泛型类型与需要使用生命周期参数的特征绑定在一起? - How do I bound a generic type with a trait that requires a lifetime parameter if I create the reference inside the function? 如何实现泛型类型的特征? - How to implement a trait for generic type? 如何编写处理可为空值和引用的通用泛型类型? - How to write universal generic type handling nullable values and references? 如何使用使用生命周期名称参数化的特征绑定声明泛型函数? - How to declare a generic function with a trait bound that is parameterized with a lifetime name? 如何为类型引用的操作指定通用特征? - How do I specify a generic trait for operations on references to types? 如何将泛型特征设置为函数参数的类型? - How to set generic trait as type of function argument?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM