Dealing with Cow fields when implementing a copy-free parser .to_owned()

Hello there everyone,

I've been giving a shot on implementing a parser using Regex into a structure, and I'm trying to keep it copy-free as much as possible. Using references to the string used on the input is fine, but as soon as I've tried to use Cow's to allow the structure itself to become owned, I couldn't continue.

I'm trying the following (ignore unwraps as this is just a test):

use regex::Regex;
use std::borrow::Cow;
use lazy_static::lazy_static;


lazy_static! {
static ref VERSION_REGEX: Regex = Regex::new(r#"([a-z]):?([1-9])?"#).unwrap();
}

#[derive(Debug, Eq, PartialEq)]
struct Parsed<'a> {
    alpha: Cow<'a, str>,
    number: Option<Cow<'a, str>>,
}

impl<'a> Parsed<'a> {
    fn parse(input: &str) -> Parsed {
        let captures = VERSION_REGEX.captures(input).expect("Should work");
        let alpha = captures.get(0).map(|x| x.as_str()).unwrap();
        let version = captures.get(1).map(|x| x.as_str());
        
        Parsed {
            alpha: alpha.into(),
            number: version.map(Cow::from),
        }
    }
    
    fn to_owned(&self) -> Parsed<'static> {
        Parsed {
            alpha: self.alpha.to_owned(),
            number: self.number.as_ref().map(Cow::to_owned)
        }
    }
}

fn main() {
    let borrowed = Parsed::parse("a:2");
    println!("{:#?}", borrowed);

    let x : String = "a:1".into();
    let borrowed = Parsed::parse(&x);
    println!("{:#?}", borrowed);
    
    let owned = {
        let x : String = "a:1".into();
        Parsed::parse(&x).to_owned()
    };
    println!("{:#?}", owned);

}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:30:31
   |
30 |             alpha: self.alpha.to_owned(),
   |                               ^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime 'a as defined on the impl at 16:6...
  --> src/main.rs:16:6
   |
16 | impl<'a> Parsed<'a> {
   |      ^^
   = note: ...so that the types are compatible:
           expected &std::borrow::Cow<'_, str>
              found &std::borrow::Cow<'a, str>
   = note: but, the lifetime must be valid for the static lifetime...
   = note: ...so that the expression is assignable:
           expected Parsed<'static>
              found Parsed<'_>

error: aborting due to previous error

error: Could not compile `playground`.

To learn more, run the command again with --verbose.

Do I need to box my struct on the return? I thought that using Cow::to_owned would allow me to make the inner data into String and then there would be no need to box the Parsed structure.

Calling to_owned on types that implement Clone (like Cow) just clones the type, so you will need to call into_owned and wrap that in a Cow

You can do that like this,

cow.clone().into_owned().into()
2 Likes

Nice! That worked!

If to_owned is clone for those types, it mean I could also make it a really really really owned method chain as well:

fn to_owned(&self) -> Parsed<'static> {
        Parsed {
            alpha: self.alpha.to_owned().into_owned().into(),
            number: self.number.to_owned().map(|x| x.into_owned().into())
        }
    }

I'm not sure how clear this is, and how idiomatic it is as well, but it looks funny enough.

edit: Actually I read this wrong, don't make this change as it won't work

self.number.to_owned().map(|x| x.into_owned().into())

This could be

self.number.as_ref().map(|x| x.into_owned().into())

to avoid unnecessary clones.

1 Like