Is it possible to avoid write this same function twice?

I want to return Vec<(T, usize)> if T implemented Copy, but if it does not implemented Copy, I will return &T.

Or what is the convention to implement such function in rust?

pub fn occurance<T>(items : &[T]) -> Vec<(&T, usize)>
    where T : Eq + PartialEq + Hash {
    let mut map : HashMap<&T, usize> = HashMap::new();
    for item in items {
        map.entry(item)
            .and_modify(|c| *c += 1)
            .or_insert(1);
    }
    map.into_iter().collect()
}

pub fn occurance_cloned<T>(items : &[T]) -> Vec<(T, usize)>
    where T : Eq + PartialEq + Hash + Copy {
    let mut map : HashMap<&T, usize> = HashMap::new();
    for item in items {
        map.entry(item)
            .and_modify(|c| *c += 1)
            .or_insert(1);
    }
    map.into_iter().map(|(k, c)| (*k, c)).collect()
}

Given that the only difference looks to be in the HashMap → Vec conversion, I'd have the function return the HashMap directly, then have the conversion be a separate step.

return HashMap<&T, usize> will not help since for code where I use this result, I will still need to convert to Vec<(&T, usize)>, or other place Vec<(T, usize)>.

Is the suffix _cloned an acceptable convention in rust? Since there are functions get, get_mut etc.

Or should I define an Occurance trait for these two types?

Well, in that case, I'd probably do something like:

fn occurance_map<T>(items : &[T]) -> HashMap<&T, usize> ...
pub fn occurance<T>(items: &[T]) -> Vec<(&T, usize)> ...
pub fn occurance_copied<T>(items: &[T]) -> Vec<(T, usize)> ...

That avoids duplicating code while still giving you both variants. I'd also use copied rather than cloned since you're depending on Copy, not Clone. And yes, having suffixed variations of functions is entirely reasonable.

As an alternative, you could also just have the one that returns a HashMap, then write an extension trait with, for example, into_vec and into_vec_copied_key methods.

Another alternative would be to have occurance return an opaque type that contains the HashMap, then give that type the methods (rather than being on a trait). That way, these conversion methods only exist specifically for the result of occurance, rather than for all coincidentally similar HashMaps.

But yes, having two functions with appropriate names is fine.

2 Likes

Thank you very much!

You could generalize your function to take I: IntoIterator and not have to worry about Copy or reference types (leave that up to the caller).

pub fn occurence<I>(iter: I) -> Vec<(I::Item, usize)>
where
    I: IntoIterator,
    I::Item: Eq + PartialEq + Hash,
{
    let mut map = HashMap::new();
    for item in iter {
        map.entry(item)
            .and_modify(|c| *c += 1)
            .or_insert(1);
    }
    map.into_iter().collect()
}
4 Likes

Wow, this is better solution, thank you!

now I can call with occurence(items) or occurence(&items).

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.