I want to implement a generic fibonacci
function that works with any type implementing Zero
, One
, and AddAssign
. I first implemented a version that works fine, but is specialized for num::BigUint
(see on play.rust-lang.org). I than came up with the following generic implementation (see on play.rust-lang.org):
extern crate num;
use num::{One, Zero};
use std::mem::swap;
use std::ops::AddAssign;
fn fib<T: Zero + One + AddAssign<&T>>(n: usize) -> T {
let mut f0 = Zero::zero();
let mut f1 = One::one();
for _ in 0..n {
f0 += &f1;
swap(&mut f0, &mut f1);
}
f0
}
This doesn't compile:
error[E0106]: missing lifetime specifier
--> src/main.rs:7:34
|
7 | fn fib<T: Zero + One + AddAssign<&T>>(n: usize) -> T {
| ^ expected lifetime parameter
Rust wants me to add a lifetime parameter to AddAssign<&T>
but I don't know how to express the lifetime of f1
.
... "the lifetime of a parameter ends when the function in which it is defined returns ", the suspend indeed returns to the caller as per control flow returns to the current coroutine caller or resumer.
Introducing lifetimes Lifetimes are what the Rust compiler uses to keep track of how long references are valid for. Checking references is one of the borrow checker's main responsibilities. Lifetimes help the borrow checker ensure that you never have invalid references.
A static is never "inlined" at the usage site, and all references to it refer to the same memory location. Static items have the static lifetime, which outlives all other lifetimes in a Rust program. Static items may be placed in read-only memory if the type is not interior mutable.
GATs (generic associated types) were originally proposed in RFC 1598. As said before, they allow you to define type, lifetime, or const generics on associated types. If you're familiar with languages that have "higher-kinded types", then you could call GATs type constructors on traits.
You need to use Higher Rank Trait Bounds. This one means basically "For any lifetime 'a
, T
satisfies the AddAssign<&'a T>
trait":
fn fib<T>(n: usize) -> T
where
for<'a> T: Zero + One + AddAssign<&'a T>,
I also had to change the way fib
is called because the compiler couldn't figure out the return type, which could be literally any type that implements those traits. Declaring x
's type gives sufficient context to the compiler so that it knows what you want.
fn main() {
let x: num::BigUint = fib(10);
// let x = fib::<BigUint>(10); // Also works
println!("fib(10) = {}", x);
}
playground
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With