How to impl Default for struct containing Cell/RefCell

Hello,
I have a hypothetical struct as following:

    struct ImmuStruct1 {
        f1: String,
        f2: Cell<usize>,
        f3: RefCell<usize>
    }

Now I want to implement Default for the above ImmuStruct1 so that I can use .take()?
I have tried several tweeks but not success, the latest try is:

    impl<'a> Default for &'a ImmuStruct1 {
        fn default() -> &'a ImmuStruct1 {
            static D: ImmuStruct1 = ImmuStruct1 {
                f1: String::new(),
                f2: Cell::new(0),
                f3: RefCell::new(0)
            };

            &D
        }
    }

Thanks,

The Default implementation I'd expect for ImmuStruct1 would be:

struct ImmuStruct1 {
    f1: String,
    f2: Cell<usize>,
    f3: RefCell<usize>,
}

impl Default for ImmuStruct1 {
    fn default() -> ImmuStruct1 {
        ImmuStruct1 {
            f1: String::new(),
            f2: Cell::new(0),
            f3: RefCell::new(0),
        }
    }
}

Which incidentally is equivalent to just deriving Default for it:

#[derive(Default)]
struct ImmuStruct1 {
    f1: String,
    f2: Cell<usize>,
    f3: RefCell<usize>,
}

Playground.

4 Likes

This implements Default for the reference, not the struct itself. To create a reference from the thin air (not from the argument), you must have some 'static value somewhere to refer to - an actual static, a leaked Box, or something like that.

3 Likes

You can use the implementation of the Default method of each type for example, like this:

impl Default for ImmuStruct1 {
	fn default() -> Self {
		Self { 
			f1: Default::default(), 
			f2: Default::default(), 
			f3: Default::default() 
		}
	}
}
1 Like

It's even easier, just derive the impl.

The problem is, as discussed, you cannot return a reference from a function that points into its own stack frame. You can create a reference in the caller's stack frame, instead.

Thank you. Now if I have another wrapping struct like :

struct ImmuStruct2 {
        f1: String,
        f2: Cell<ImmuStruct1>
    }
let is2 = ImmuStruct2 {
        f1: "is2_f1".to_string(),
        f2: Cell::new(ImmuStruct1 {
                                     f1: "is2_f2_f1". to_string(),
                                     f2: Cell::new(2222),
                                     f3: RefCell::new(3333)})
};

How can I get and use is2.f2.f2 e.g. for println! while leaving other values as-is? If I use is2.f2.take() then is2.f2 will be left with default value, or if using .into_inner() then it consumes is2.f2.

Thanks,

In your example where you have ownership, you can

let mut is2 = ImmuStruct2 { ... };
println!("{}", is2.f2.get_mut().f2.get_mut());

More generally, you can

impl ImmuStruct2 {
    fn get_f2(&self) -> usize {
        let tmp = self.f2.take();
        let value = tmp.f2.get();
        self.f2.set(tmp);
        value
    }
}

println!("{}", is2.get_f2());

&Cell<T>s don't support borrowing &Ts, because it wouldn't be able to ensure they were all inactive when you .set a new value.

1 Like

Thanks, but I don;t want to make is2 mutable entirely. I need it immutable with just some mutable fields i.e. with Cell/RefCell. Although your get_f2 works, in fact I tried the same approach with the whole f2 i.e. create a temporary then get then set back, is there another way more elegant that we don't have to do that temporary variable trick?

There's no other sound way with the structs provided.

Cell<T> is most ergonomic when T: Copy. Otherwise you generally have to move the T in and out of the Cell.

If you want to be able to get references without moving in and out of the container, use RefCell.

Yah, that;s why I put f3 in the struct. I want to experiment and compare Cell & RefCell. Thanks.