2

In Rust, how can I pass a vector of owned objects to a function that expects a vector of borrowed objects? Is my only option to create a new vector?

What is the best practice for the signature of a function in which I care about the type of the contained generic of a struct but don't care about if it is borrowed or not?

Example situation:

fn using_vec_of_borrows(borrows: &Vec<&String>) {
    //...
}

fn main() {
    let foo: Vec<String> = Vec::new();
    using_vec_of_borrows(&foo);
}

How could I write using_vec_of_borrows() so that it accepts a vector of borrowed strings or a vector of owned strings? Or if it was part of an external library, could I convert my vector of owned strings to a vector of borrowed strings without iterating over it?

Keep in mind here that String is just used as an example type. It could be anything.

kmdreko
  • 42,554
  • 6
  • 57
  • 106
Barracuda
  • 332
  • 4
  • 17
  • Does this answer your question [How to get a slice of references from a vector in Rust?](/q/37797242/2189130) You need to create a new `Vec` via `foo.iter().collect()`. See the fix applied on the [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f7e0e913a8e007afaec2ba4d7eb8b446). – kmdreko Sep 24 '22 at 18:43
  • No it doesn't, because the question asks if it is possible to have a function that accepts both borrowed content and owned content, and also if it is possible to make a vector of owned content pass as a vector of borrowed without new iterattion. – Barracuda Sep 24 '22 at 19:01
  • There seem to be multiple questions. You should focus on one per post, but to answer these: no, it's not possible for a function to accept both borrowed content and owned content, BUT it is possible for the content to be either borrowed or owned (see [cow](https://doc.rust-lang.org/std/borrow/enum.Cow.html)). In general, if you have a `Vec`, it's not possible to get a `Vec<&T>` without recreating it. Picturing yourself the memory layout should make that clear. – jthulhu Sep 24 '22 at 19:04
  • @BlackBeans thanks! kmdreko shown however in his answer that it is indeed possible to have a method that takes either owned or not owned vector content. – Barracuda Sep 24 '22 at 19:24
  • @Barracuda yes, you could have accomplished something similar with Cow, depending on what the wanted semantic. But there is a difference between "a function that accepts both &T and T" and "a function that accepts a Something" where `Something` can have the semantics of `&T` or `T`, which was my point (the first is impossible, the second is possible). – jthulhu Sep 24 '22 at 20:08
  • Very fair, and yes I do unterstand your point that at a low level, it's not possible. – Barracuda Sep 24 '22 at 21:13

1 Answers1

3

Quoting @trent from this answer:

When you hear "generic over references and non-references", think Borrow<T>.

Making your function generic over elements that implement Borrow<String> will allow it to accept both a &Vec<String> and &Vec<&String>:

use std::borrow::Borrow;

fn using_vec_of_borrows<T: Borrow<String>>(borrows: &[T]) {
    //...
}

fn main() {
    let foo: Vec<String> = Vec::new();
    using_vec_of_borrows(&foo);
    
    let foo: Vec<&String> = Vec::new();
    using_vec_of_borrows(&foo);
}

You can even change it to an iterator for more flexibility if the elements don't need to be contiguous.

Note: I changed the signature from &Vec<T> to &[T] since the latter is more idiomatic. And I would've changed Borrow<String> into Borrow<str> for a similar reason, but &String does not implement Borrow<str>.

See also:

kmdreko
  • 42,554
  • 6
  • 57
  • 106
  • Thank you, this answered my question about the method signature! – Barracuda Sep 24 '22 at 19:23
  • I try to use the Borrow solution, but it is not working with complex structs like Regex (from the crate of the same name). I can't call methods implemented by the struct. I get an error `no method named 'is_match' found for reference &R in the current scope`. https://gist.github.com/rust-play/23b73d813bc55ad38bb62d8c57be8f13 – Barracuda Sep 25 '22 at 01:03
  • NVM I was dumb, I need to add an explicit call to `.borrow()` to use it. A bit annoying. – Barracuda Sep 25 '22 at 01:05