How do I convert a Vec<&str> to &Vec<&str>

Hello,

I have the following code:

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HostSet(HashSet<String>);

impl HostSet {
    pub fn new() -> Self {
        Self(HashSet::new())
    }

    pub fn diff<'a>(
        &'a self,
        other: &'a HostSet,
    ) -> (
        Difference<'a, String, RandomState>,
        Difference<'a, String, RandomState>,
    ) {
        (self.0.difference(&other.0), other.0.difference(&self.0))
    }

    pub fn update<T>(&mut self, other: T) -> &mut Self
    where
        T: IntoIterator,
        String: From<<T as IntoIterator>::Item>,
    {
        self.0 = HashSet::<String>::from_iter(other.into_iter().map(|i| String::from(i)));

        self
    }
}

and trying to call

    fn test_host_set() {
        let old_hosts_raw = vec!["host1", "host2", "host3"];
        let mut old_hosts = HostSet::new();
        old_hosts.update(&old_hosts_raw);
        // some use of old_hosts_raw
    }

which will not work since the type of old_hosts_raw is Vec<&str>, and in HostSet.update, i in the map has type &&str rather than &str. Removing & in the &old_hosts_raw will cause any further use of old_hosts_raw illegal. I am wondering:

  1. There should be some custom of writing such kind of code, just like in C++
std::transform(std::begin(other), std::end(other), std::back_inserter(container), |i| -> auto { return Container::value_t(i); });

which I do not know. Can anyone give me a hint?

  1. How do I fix this issue?

Solution

-old_hosts.update(&old_hosts_raw);
+old_hosts.update(old_hosts_raw.iter().copied());

Rust Playground

Reason

As the error points out, IntoIterator::Item is &&str, not &str which <std::string::String as From<&str>> requires.

To have a Iterator that yields &str from one that yields &&str, use .copied() or .cloned() or .map(|s| *s).

error[E0277]: the trait bound `std::string::String: From<&&str>` is not satisfied
  --> src/lib.rs:39:22
   |
39 |     old_hosts.update(old_hosts_raw.iter());
   |               ------ ^^^^^^^^^^^^^^^^^^^^ the trait `From<&&str>` is not implemented for `std::string::String`
   |               |
   |               required by a bound introduced by this call
   |
   = help: the following other types implement trait `From<T>`:
             <std::string::String as From<char>>
             <std::string::String as From<Box<str>>>
             <std::string::String as From<Cow<'a, str>>>
             <std::string::String as From<&str>>
             <std::string::String as From<&mut str>>
             <std::string::String as From<&std::string::String>>
note: the method call chain might not have had the expected associated types
  --> src/lib.rs:39:36
   |
37 |     let old_hosts_raw = vec!["host1", "host2", "host3"];
   |                         ------------------------------- this expression has type `Vec<&str>`
38 |     let mut old_hosts = HostSet::new();
39 |     old_hosts.update(old_hosts_raw.iter());
   |                                    ^^^^^^ `IntoIterator::Item` is `&&str` here
note: required by a bound in `HostSet::update`
  --> src/lib.rs:28:17
   |
25 |     pub fn update<T>(&mut self, other: T) -> &mut Self
   |            ------ required by a bound in this associated function
...
28 |         String: From<<T as IntoIterator>::Item>,
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `HostSet::update`

Alternative solution

Change the signature of update

-String: From<<T as IntoIterator>::Item>,
+<T as IntoIterator>::Item: AsRef<str>,

Rust Playground

8 Likes

Arrays of Copy elements are also Copy, so you can

fn test_host_set() {
    let old_hosts_raw = ["host1", "host2", "host3"];
    let mut old_hosts = HostSet::new();
    old_hosts.update(old_hosts_raw.into_iter());
    // still usable
    old_hosts_raw;
}

if you don't need a Vec specifically.

1 Like

Note that you should never have a &Vec<_> if you can help it. A &[_] is both more flexible and more performant.

If you're thinking about a back_inserter, you'll probably be wanting Extend in std::iter - Rust

And the std::transform is done more with Iterator::map.

So the equvalent of that C++ would be something like container.extend(other.map(…)).

2 Likes

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.