Generic function for int, float, char or string in vector


#1

Hello

I am a beginner and I would like to a generic function that works for all of the vectors (longer_vector) and not to write a function for each datatype (longer_vector_int, longer_vector_string…).

Example Code:

fn main() {
let v1 = vec![‘a’,‘b’,‘c’,‘d’];
let v2 = vec![‘x’,‘y’,‘z’];

println!("{:?}", longer_vector_char(&v1, &v2));

let v3 = vec![1,2,3,4,5,6];
let v4 = vec![1,2,3,4,5,6,7];

println!("{:?}", longer_vector_int(&v3, &v4));

let v5 = vec!["xTEST1".to_string(),"TEST2".to_string(),"TEST3".to_string(),"TEST4".to_string()];
let v6 = vec!["TestX".to_string(), "TestY".to_string(), "TESTzzzz".to_string()];

println!("{:?}", longer_vector_string(&v5, &v6));

println!("{:?}", longer_vector(&v1, &v2));
println!("{:?}", longer_vector(&v3, &v4));
println!("{:?}", longer_vector(&v5, &v6));

}

// THIS DOESN’T SEEM TO WORK, WHAT AM I DOING WRONG
fn longer_vector<T: 'a>(x: &'a[T], y: &'a[T]) -> &'a[T] {
if x.len() > y.len() { x } else { y }
}

fn longer_vector_int<'a>(x: &'a[i32], y: &'a[i32]) -> &'a[i32] {
if x.len() > y.len() { x } else { y }
}

fn longer_vector_char<'a>(x: &'a[char], y: &'a[char]) -> &'a[char] {
if x.len() > y.len() { x } else { y }
}

fn longer_vector_string<'a>(x: &'a[String], y: &'a[String]) -> &'a[String] {
if x.len() > y.len() { x } else { y }
}

thank you for your help

Marc


#2

The compiler tells you exactly what you’re doing wrong:

error[E0261]: use of undeclared lifetime name `'a`
  --> src/main.rs:22:29
   |
22 | fn longer_vector<T: 'a>(x: &'a[T], y: &'a[T]) -> &'a[T] {
   |                             ^^ undeclared lifetime

Fixed:

fn longer_vector<'a, T: 'a>(x: &'a[T], y: &'a[T]) -> &'a[T] {
    if x.len() > y.len() { x } else { y }
}

As an aside: when posting something, check the formatting in the preview before posting. When you’re posting code, try to link to the playpen.


#3

Thank you

i’m a very beginner, :slight_smile:


#4

Hello Daniel

is there any Documentation for such Generic functions, i just found https://doc.rust-lang.org/1.8.0/book/generics.html#generic-functions

I still don’t understand why the first 'a parameter in <> has no Type and the second has one, and i also don’t understand how/if these are stacked somehow because its a vector (where i guess thats the first 'a) that contains values (the second 'a) from Type (T). And i also found things like <T as Sub… what just confuses me more.

As i explained I’am a very beginner, i started Friday with rust… so if you know a lecture that focus on this detail of rust this would be very helpful.

with kind regards
Marc


#5

That’s an old version of the book (as denoted by the version number in the URL). You should make sure to read through the latest version of the book. I really, really don’t recommend skipping stuff; there are a number of things in Rust that people tend to skip over learning, causing themselves significant problems later on (modules, traits, and generics are the big ones).


#6

The function longer_vector<'a, T: 'a> declares two type parameters: a lifetime 'a, and a generic type T which must be given an argument which will valid for the lifetime 'a. (This T: 'a notation is called a bound on T.) The reason for this is that we’re taking in references (slices) to data as arguments, and the compiler needs to ensure that the data will live at least as long as the reference.

i also don’t understand how/if these are stacked somehow because its a vector (where i guess thats the first 'a) that contains values (the second 'a) from Type (T)

A Vec<T> contains values of type T. When we create a slice &[T] to the contents of that vector, it acts as a reference to its contents. As noted above, the values must outlive the slice, so if we call the lifetime of those values 'a, we have T: 'a ("T lives at least as long as 'a") and &'a [T] denotes a slice that is only valid for the duration 'a.

And i also found things like <T as Sub… what just confuses me more.

Typically, you’ll see something like <T as Trait> only in a context where we’ve declared T: Trait, which means any T passed in as an argument must implement the trait Trait. What this syntax, we can then unambiguously invoke methods or refer to associated types and constants of the trait Trait. The problem it solves is that a type may implement multiple traits with the same method/type/constant name.

With Sub, things might get a little more confusing, because Sub declares an associate type. Somthing like this:

pub trait Sub<T> {
   type Output;
    /// ...
}

This means that we have to specify the value of Output any time we use Sub as a bound on a type. Here, we declare a function which allows us to subtract two values so long as they are the same type and the result of the subtraction is the same type.

fn generic_subtract<T: Sub<T, Output=T>>(a: T, b: T) -> T::Output {
    a - b
}

We could make it ambiguous by adding another Output parameter in the bounds:

fn generic_subtract<T: Sub<T, Output=T> + Add<T, Output=i32>>(a: T, b: T) -> <T as Sub>::Output {
    a - b
}

This does the same thing, except it only works for those T which add into an i32. The T as Sub allows us to specify which Output we’re actually using.