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
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
BTW: lifetime elision
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
BTW: What is the difference between Cow::as_ref()
and Cow::borrow()
???
dEajL3kA:
#[derive(Debug)]
pub enum Value<'buf> {
Single(Cow<'buf, str>),
Multiple(Vec<Cow<'buf, str>>),
}
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
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
system
Closed
April 9, 2023, 7:50pm
9
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.