How to convert a Vec<Cow<str>> into Vec<&str>?

Suppose I have a struct like this:

#[derive(Debug)]
pub struct QueryString<'buf> {
    data: HashMap<Cow<'buf, str>, Value<'buf>>,
}

#[derive(Debug)]
pub enum Value<'buf> {
    Single(Cow<'buf, str>),
    Multiple(Vec<Cow<'buf, str>>),
}

impl<'buf> QueryString<'buf> {
    pub fn get(&self, key: &str) -> Option<&Value> {
        self.data.get(key)
    }
}

...is there a way to return the values as Option<Vec<&'buf str>> directly ???

In other words: How to implement this?

impl<'buf> QueryString<'buf> {
    pub fn get_ex(&self, key: &str) -> Option<Vec<&'buf str>> {
        match self.data.get(key) {
            Some(value) => match(value) {
                Value::Single(single) => /* ?????? */,
                Value::Multiple(multiple) => /* ?????? */,
            },
            None => None
        }
    }
}

Thank you for any ideas!


EDIT: I tried this, but it doesn't work :thinking:

impl<'buf> QueryString<'buf> {
    pub fn get_ex(&self, key: &str) -> Option<Vec<&'buf str>> {
        match self.data.get(key) {
            Some(value) => Some(match value {
                Value::Single(single) => vec![single.as_ref()],
                Value::Multiple(multiple) => multiple.iter().map(Cow::as_ref).collect(),
            }),
            None => None
        }
    }
}

The error I get is:

^ associated function was supposed to return data with lifetime `'buf` but it is returning data with lifetime `'1`

You have the wrong lifetime in the output. It should be:

    pub fn get_ex<'a>(&'a self, key: &str) -> Option<Vec<&'a str>> {

Or, using lifetime elision:

    pub fn get_ex(&self, key: &str) -> Option<Vec<&str>> {
6 Likes

Thanks! So the life-time specifier must be placed at the &self parameter :bulb: :+1:

BTW: lifetime elision :exploding_head:

Yes. Because of the Cow, the strings are not necessarily in the buffer (whose lifetime is 'buf), they may be owned by the Value, and thus, by the QueryString that owns the Value. The lifetime of QueryString may be shorter than 'buf.

1 Like

After seeing a few of your posts, your code might end up a bit cleaner if you define some helper methods on Value. For example:

use std::collections::HashMap;
use std::borrow::Borrow;
use std::borrow::Cow;

#[derive(Debug)]
pub enum Value<'buf> {
    Single(Cow<'buf, str>),
    Multiple(Vec<Cow<'buf, str>>),
}

impl<'buf> Value<'buf> {
    pub fn as_slice(&self)->&[Cow<'buf, str>] {
        match self {
            Self::Single(cow) => std::slice::from_ref(cow),
            Self::Multiple(v) => v
        }
    }
    
    pub fn iter(&self)->impl Iterator<Item=&str> {
        self.as_slice().iter().map(Cow::borrow)
    }
}

#[derive(Debug)]
pub struct QueryString<'buf> {
    data: HashMap<Cow<'buf, str>, Value<'buf>>,
}

impl<'buf> QueryString<'buf> {
    pub fn get_ex(&self, key: &str) -> Option<Vec<&str>> {
        Some(self.data.get(key)?.iter().collect())
    }
}
4 Likes

Yeah, adding an iter() method to Value definitely seems like a good idea :grinning:

BTW: What is the difference between Cow::as_ref() and Cow::borrow() ???

This doesn't really answer your question, but this type looks very much like a SmallVec<[Cow<'buf, str>; 1]>.

You might try using that (or TinyVec or one of the many other such crates) to let someone else write the .iter() function and such for you :slightly_smiling_face:

2 Likes

There's no difference in this case. AsRef and Borrow are similar traits with different intentions (if you don't meet the documented requirements for Borrow, odd and annoying things may happen in code that relies on them, like in a HashMap).

1 Like