Function that can take a slice of `&str` or `String`

Is it possible to have a function that can take a slice of &str or String via Into<String> or some other way. Something like this (not working):

fn main() {
    generic_string_slice(&["Hello", "World"]);
    generic_string_slice(&["Hello".to_string(), "World".to_string()]);
}

fn generic_string_slice<T>(list: &[T])
where
    T: Into<String>,
{
    let list: Vec<String> = list.into_iter().map(|s| s.into()).collect();
    dbg!(list);
}

Yes, there's AsRef<str> trait for this: &[impl AsRef<str>] and call .as_ref().

and you can make it even more generic by taking I: IntoIterator<Item=S>, S: AsRef<str>.

4 Likes

Thank you. That works:

fn generic_string_slice(list: &[impl AsRef<str>]) {
    let list: Vec<String> = list.iter().map(|s| s.as_ref().to_string()).collect();
    dbg!(list);
}

Out of interest, is it possible to do something similar with &[impl Into<String>]?

Into<String> works too. Call .into() instead of .as_ref().

However, &[T] is borrowing items, so it can't give you an owned string. Take Vec<T> or IntoIterator to be able to take the strings without copying them.

1 Like

What would that look like? I tried the following (and different variants) but it has error[E0282]: type annotations needed but I can't see how to annotate the into().

fn generic_string_slice2(list: &[impl Into<String>])
{
    let list: Vec<String> = list.iter().map(|s| *s.into()).collect();
    dbg!(list);
}

I though the turbo fish might work but this doesn't work either:

fn generic_string_slice2(list: &[impl Into<String>])
{
    let list: Vec<String> = list.iter().map(|s| Into::<String>::into(s)).collect();
    dbg!(list);
}

That was literally in the second paragraph. You can't move out of a reference. You'll need impl IntoIterator<Item = impl Into<String>>.

2 Likes

This is because Into is destructive (the original object is gone, and reused if possible).

But any type with & in it, recursively, promises that it won't take or destroy anything [1]. That applies to slices. Dereferencing can't break that promise. It can copy primitive types, but not heap-allocated strings.

So if you want to turn objects into strings by destroying the originals, then you need a Vec or another type that will let you take ownership.

If you're not taking ownership of the collection, then AsRef is more appropriate.


  1. except interior mutability types ↩ī¸Ž

1 Like

@kornel Thanks for the detailed explanation. That makes perfect sense.

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.