rwf2 / cookie-rs

HTTP cookie parsing and cookie jar management for Rust.
https://docs.rs/cookie
Apache License 2.0
311 stars 117 forks source link

What is the expected way to clone a Cookie? #72

Closed Frederick888 closed 7 years ago

Frederick888 commented 7 years ago

How to clone a Cookie to a specific lifetime? For example,

struct NaiveCookieJar<'a> {
    data: HashMap<(String, String), Cookie<'a>>,
}

impl<'a> NaiveCookieJar<'a> {
    fn add(&mut self, domain: &str, name: &str, cookie: &Cookie) -> bool {
        /* this works for static 'a
        self.data
            .insert((String::from(domain), String::from(name)),
                    cookie.clone().into_owned())
            .is_some()
        */
        self.data.insert((String::from(domain), String::from(name)), cookie.clone()).is_some()
    }
}

This code resulted in error: E0495: cannot infer an appropriate lifetime for lifetime parameter 'c due to conflicting requirements

It seems that cookie.clone() is not working because Cookie holds a Cow<'a, str>. Then what is the proper way to do this?

alexcrichton commented 7 years ago

Ah yeah you've found that into_owned will work here, but otherwise you'll have to restrict the incoming cookie as cookie: &Cookie<'a>

Frederick888 commented 7 years ago

@alexcrichton But what if the jar lives any longer than the cookie to add? E.g.

#[derive(Debug, Clone)]
struct NaiveJar<'a> {
    data: Vec<Cookie<'a>>,
}

impl<'a> NaiveJar<'a> {
    fn add(&mut self, cookie: &Cookie) {
        self.data.push(cookie.clone());
    }
}

fn main() {
    let mut jar = NaiveJar { data: Vec::new() };
    {
        let cookie = Cookie::new("session_id", "abcde");
        jar.add(&cookie);
    }
    println!("{:?}", jar);
}

This might be a little off-topic as it actually seems to be a general question, like

use std::borrow::Cow;

#[derive(Debug, Clone)]
struct MyStruct<'a> {
    s: Option<Cow<'a, str>>
}

impl<'a> MyStruct<'a> {
    pub fn new() -> MyStruct<'a> {
        MyStruct {
            s: None
        }
    }

    pub fn set(&mut self, s: &str) {
        self.s = Some(Cow::Owned(s.to_owned()));
    }
}

#[derive(Debug, Clone)]
struct MyStructWrapper<'a> {
    d: MyStruct<'a>
}

impl<'a> MyStructWrapper<'a> {
    pub fn new() -> MyStructWrapper<'a> {
        MyStructWrapper {
            d: MyStruct::new()
        }
    }

    pub fn set_data(&mut self, s: &MyStruct) {
        self.d = s.clone(); // we want this clone to live 'a, if any referenced data of "s" don't, we'd like to copy those data as well
    }
}

fn main() {
    let mut my_struct_wrapper = MyStructWrapper::new();
    {
        let mut my_struct = MyStruct::new();
        my_struct.set("hello world");
        my_struct_wrapper.set_data(&my_struct);
    }
    println!("{:?}", my_struct_wrapper);
}
Frederick888 commented 7 years ago

@alexcrichton Wait... I have just experienced an epiphany in https://www.reddit.com/r/rust/comments/5qz9fg/hey_rustaceans_got_an_easy_question_ask_here_52017/dd9sfsz/?context=3 .

The guy told me that a static datum doesn't necessarily live as long as the running program as describe in http://rustbyexample.com/scope/lifetime/static_lifetime.html

A 'static lifetime is longest possible lifetime, and lasts for the lifetime of the running program. A 'static lifetime may also be coerced to a shorter lifetimes.

...or https://doc.rust-lang.org/book/lifetimes.html#static

The lifetime named ‘static’ is a special lifetime. It signals that something has the lifetime of the entire program.

The statement above indicates that static itself is as long as the program, but it doesn't coerce every static thing to be the same. They can live that long, but they can also get dropped as others. For example, the following program won't use up your memory as the data referenced by a is dropped when a goes out of the scope.

struct MyStruct<'a>(Cow<'a, str>);

impl<'a> MyStruct<'a> {
    pub fn new(s: &str) -> MyStruct<'static> {
        MyStruct(Cow::Owned(s.to_owned()))
    }
}

fn main() {
    for i in 0..usize::max_value() {
        let a = MyStruct::new(i.to_string().as_str());
    }
}

If this is true then I don't need to bother manipulating the lifetime and use into_owned() directly.

alexcrichton commented 7 years ago

Yeah you don't have to take a Cookie<'a>, but rather you can use into_owned to take a cookie of any lifetime, promote it to 'static, and then put it into a container. Does that work for you here?

Frederick888 commented 7 years ago

@alexcrichton Yup, thanks. Apparently I have misunderstood the document ;) The "It signals that something has the lifetime of the entire program" has been misleading me for almost a year since the day I started to learn Rust LOL.

alexcrichton commented 7 years ago

Glad it's working now!