Using std::cell::Cell with a String

I'm trying use a String Cell in my project but I am having trouble with the get method. Also I don't know enough about Cell's. Should the functions use String or &str's?

Using the Rust Playground this example says
the following trait bounds were not satisfied: String: Copy

use std::cell::Cell;

pub struct Object {
    cell: Cell<String>,
}
impl Object {
    pub fn new(title: String) -> Self {
        let cell = Cell::new(title);
        Object { cell }
    }
    
    pub fn title(&self) -> String {
        self.cell.get()
    }
    
    pub fn set_title(&self, new_title: String) {
        self.cell.replace(new_title);
    }
}
fn main() {
    let obj = Object::new("it's me!".to_string());
    println!("{}", obj.title());
    
    obj.set_title("yeah :)".to_string());
    println!("{}", obj.title());
}

Cell is only really useful for types that implement Copy, which String doesn't. You might use std::cell::RefCell instead.

(On mobile right now, so can't get into more detail)

1 Like

In general: The most useful thing you can do on a Cell with a non-Copy type is to swap the value. E.g. use the take method to replace the string in the cell with an empty one temporarily, access the taken-out string, and use set to put back the (potentially modified) String.


Your concrete method can this be re-written as

pub fn title(&self) -> String {
    let title = self.cell.take();
    let title_cloned = title.clone();
    self.cell.set(title); // put back title
    title_cloned // return the clone
}

Obviously, this clones the string, which might be somewhat expensive. RefCell is more powerful than Cell (but has a small overhead, and can result in panics if you use it wrong). With RefCell you couldn't make your API as it is "more efficient" either, but it would probably be more ergonomic to use than the whole take+put dance.

2 Likes

Some example code for the same thing with RefCell:

pub struct Object {
    cell: RefCell<String>,
}
impl Object {
    pub fn new(title: String) -> Self {
        let cell = RefCell::new(title);
        Object { cell }
    }

    pub fn title(&self) -> String {
        self.cell.borrow().clone()
    }

    pub fn set_title(&self, new_title: String) {
        *self.cell.borrow_mut() = new_title;
    }
}
1 Like