Skip to content

Add shallow_clone() method to Cow which always reborrows data #283

@getreu

Description

@getreu

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions