Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Generic longer function for types implementing len() function in Rust

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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(...) {
...
}

>Solution :

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)

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading