56

Per Steve Klabnik's writeup in the pre-Rust 1.0 documentation on the difference between String and &str, in Rust you should use &str unless you really need to have ownership over a String. Similarly, it's recommended to use references to slices (&[]) instead of Vecs unless you really need ownership over the Vec.

I have a Vec<String> and I want to write a function that uses this sequence of strings and it doesn't need ownership over the Vec or String instances, should that function take &[&str]? If so, what's the best way to reference the Vec<String> into &[&str]? Or, is this coercion overkill?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Don Rowe
  • 833
  • 1
  • 7
  • 17

2 Answers2

67

You can create a function that accepts both &[String] and &[&str] using the AsRef trait:

fn test<T: AsRef<str>>(inp: &[T]) {
    for x in inp { print!("{} ", x.as_ref()) }
    println!("");
}

fn main() {
    let vref = vec!["Hello", "world!"];
    let vown = vec!["May the Force".to_owned(), "be with you.".to_owned()];
    test(&vref);
    test(&vown);
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
aSpex
  • 4,790
  • 14
  • 25
  • 6
    I think this is the answer the OP was looking for, as it allows using the slice without an unnecessary allocation. This approach is even more useful when accepting a slice of `AsRef` - you want the function to accept all of `&[&str]`, `&[String]`, `&[Path]`, and `&[PathBuf]`, without allocating new memory. – user4815162342 Dec 16 '16 at 10:59
  • One sad thing is if you're trying to take a `Option<&[T]>` you can't just pass a `None` without specifying a concret type – klefevre Jan 30 '19 at 16:27
20

This is actually impossible without either memory allocation or per-element call1.

Going from String to &str is not just viewing the bits in a different light; String and &str have a different memory layout, and thus going from one to the other requires creating a new object. The same applies to Vec and &[]

Therefore, whilst you can go from Vec<T> to &[T], and thus from Vec<String> to &[String], you cannot directly go from Vec<String> to &[&str]. Your choices are:

  • either accept &[String]
  • allocate a new Vec<&str> referencing the first Vec, and convert that into a &[&str]

As an example of the allocation:

fn usage(_: &[&str]) {}

fn main() {
    let owned = vec![String::new()];

    let half_owned: Vec<_> = owned.iter().map(String::as_str).collect();

    usage(&half_owned);
}

1 Using generics and the AsRef<str> bound as shown in @aSpex's answer you get a slightly more verbose function declaration with the flexibility you were asking for, but you do have to call .as_ref() in all elements.

rsalmei
  • 3,085
  • 1
  • 14
  • 15
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Thanks, Matthieu. For my case right now, I think I'll go with `&[String]` since I imagine allocating a new `Vec<&str>` incurs extra work. – Don Rowe Dec 16 '16 at 08:10
  • 2
    @DonRowe: It incurs an extra allocation (O(1) but potentially expensive) + conversion (O(n)). – Matthieu M. Dec 16 '16 at 08:16