-
Notifications
You must be signed in to change notification settings - Fork 24
Description
Proposal
Problem statement
When working with nom and tpnote it is crucial to avoid allocation as much as possible. I find myself frequently with a &Cow<str> I tend to clone() because I need a Cow.
The clone() method copies by default it's owned contents, although it is not always necessary. This proposal suggests a shallow clone, that never allocates memory.
Motivating examples or use cases
use crate::tpnote_lib::clone_ext::CloneExt;
use std::borrow::Cow;
fn do_something_or_nothing(v: Cow<str>) -> Cow<str> {
if v.len() > 3 {
let s = "Hello ".to_string() + &*v;
Cow::Owned(s)
} else {
v
}
}
// Sometimes, we only have a `&Cow`, but we need a `Cow`!
let a: &Cow<str> = &Cow::Owned("world!".to_string());
let b: Cow<str> = a.shallow_clone();
assert_eq!(do_something_or_nothing(b), "Hello world!");
let a: &Cow<str> = &Cow::Owned("ld!".to_string());
let b: Cow<str> = a.shallow_clone();
assert_eq!(do_something_or_nothing(b), "ld!");In the examples above you can replace .shallow_clone() with .clone() and get the same result with the difference, that .clone() might allocate memory whereas .shallow_clone() never does.
Solution sketch
use std::borrow::Cow;
pub(crate) trait CloneExt<'b> {
impl<'b> CloneExt<'b> for Cow<'b, str> {
fn shallow_clone(&'b self) -> Cow<'b, str> {
match *self {
Self::Borrowed(b) => Self::Borrowed(b),
Self::Owned(ref o) => Self::Borrowed(o.as_ref()),
}
}
}Todo: generics
Alternatives
Modify the clone() method, that the compiler decides, if a copy or reborrow is necessary. This depends on the lifetime situation of the original and the clone. The downside of this alternative is, that the user has no means to avoid explicitly memory allocation. The proposed solution is sometimes restrictive because the original must outlive the clone. In my use cases, the restriction can be bypassed easily.
Links and related work
A more general solution has been proposed here but was discarded because of this argument.