Home > other >  Generic longer function for types implementing len() function in Rust
Generic longer function for types implementing len() function in Rust

Time:07-10

What I'm attempting to do is creating generic function that compares two instances of something that has length.

My code:

fn main() {
    let vec1 = vec!(1, 2, 3);
    let vec2 = vec!(1, 2, 3, 4);
    println!("Longest vector is: {:?}", longer_vec_gen(&vec1, &vec2));
    println!("Longest iterator is {:?}", longer_exact_size_iterator(&vec1.iter(), &vec2.iter()))
}

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

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

Both of those functions works however the first one works only for Vec so I wanted to make step forward and do it for anything that has length. The second one kinda achieves this, but right now I'm forced to use .iter() on parameters while calling this function which I would love to avoid.

Another problem is that i can't pass String or string literals to this function.

What I would love to achieve is creating function longer that would take any two elements that has implemented function .len() so this code would run without errors:

fn main() {
    let vec1 = vec!(1, 2, 3);
    let vec2 = vec!(1, 2, 3, 4);
    println!("Longer vector is: {:?}", longer(&vec1, &vec2));

    let string1 = String::from("abcd");
    let string2 = String::from("xyz");
    println!("Longest string is : {:?}", longer(&string1, &string2))
}

fn longer(...) {
...
}

CodePudding user response:

but right now I'm forced to use .iter() on parameters while calling this function which I would love to avoid

You can solve that particular issue by bounding on IntoIterator. This is a bit tricky since you want to return a reference that was passed in to the function, but IntoIterator consumes its receiver. You can make this work by requiring that the type be IntoIterator Copy. Since Copy is satisfied by shared references, this function would work for references to most (if not all) standard container types:

fn longer_exact_size_iterator<T, U>(x: T, y: T) -> T
where T: IntoIterator<IntoIter=U>   Copy,
    U: ExactSizeIterator,
{
    if x.into_iter().len() > y.into_iter().len() {
        x
    } else {
        y
    }
}

Now you can do longer_exact_size_iterator(&vec1, &vec2).

(Playground)

If you want a variant that works for anything with a .len() but that may not have exact-size iterators then you'll have to write your own trait and implement it on the types you want to handle; it won't be automatic. For example:

pub trait HasLength {
    fn len(self) -> usize;
}

impl<T> HasLength for &Vec<T> {
    fn len(self) -> usize {
        Vec::len(self)
    }
}

impl<T> HasLength for &[T] {
    fn len(self) -> usize {
        <[T]>::len(self)
    }
}

// And so on...

fn longer<T: HasLength   Copy>(x: T, y: T) -> T {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

(Playground)

  • Related