I have another question regarding the QueryString struct.
I have extended to original code to actually decode the URL-encoding (percent encoding) of the query parameters. For this purpose I use the urlencoding::decode() function.
Now, I understand that I can no longer store &str in the HashMap and in the Value, because the string that comes in as &str needs to be modified when it gets decoded, so we now need an ownedString.
Changing HashMap and Value to store Strings is easy enough.
Here is what confuses me:
The urlencoding::decode() function returns a value of type Cow<str>, where I'd expect a String. I'm not exactly sure what is the purpose of Cow<str> here. To my understanding, Cow<str> is a sort of string that will be cloned automatically as soon as it is modified ("copy on write").
Anyway, I can convert Cow<str> into a "normal" String by calling .into_owned(). But is this the "proper" thing to do here? I assume that this triggers a clone() right away, correct? Is there a better alternative?
(after all, I could wrap a Cow<str> into the HashMap and in the Value, instead of a "normal" String, I think)
I think the point of Cow<str> is that the decoding process might be able to just return the original data, but if any characters are escaped it needs to create a new string.
I think the point of Cow<str> is that the decoding process might be able to just return the original data, but if any characters are escaped it needs to create a new string.
It will only clone() if it’s storing an &str; if it’s already holding a String, you’ll be handed that without any additional allocation.
Okay, this makes sense!
Still, I need all strings to be the same type, so that they can be used as key in a HashMap or stored in a Value. What should I do with the Cow<str> objects? Should I convert them all to String right away?
The alternative would be to make the HashMap and Value store Cow<str> elements. I think this would avoid having to allocate an actual String in the cases where the Cow<str> wraps the original &str. But, then, how do I implement my QueryString::get() -> Option<&Value> function ???
(After all, we don't want to expose Cow<str> to the user of QueryString, but &str or maybe String)
I think calling .into_owned() is also fine. It will allocate a new String from Cow::Borrowed, but if the code isn't performance critical, you can save yourself a lot of wrangling with lifetime annotations.
This looks fine. Perhaps this came up on a previous thread or is outside the scope of your question but why are you using a variant for Value? Some weird things can be constructed (namely a Value::Multiple with less than two items in its Vec). It seems like the right type is (Cow<str>, Vec<Cow<str>>) (the head and the tail stored separately; this guarantees you always have at least one element).
Also, I think you can avoid the take but it might be a little hairy. You can use replace to put a Multiple with an empty vector, then push the items you have. The take constructs a default value which might I think allocates an empty string.