I have a struct with function remove, and I want the remove function to return new instance if actually removed or the same reference
But I get Cannot move when trying to return *self
Is it possible?
From the errors I don’t think so, but then how Cow is possible.
Code:
use std::collections::HashMap;
#[derive(Clone)]
struct Something {
data: HashMap<char, u16>,
}
impl Something {
fn new() -> Self {
return Something {
data: HashMap::new()
}
}
fn get(&self, c: char) -> Option<&u16> {
return self.data.get(&c);
}
fn add(&self, c: char, value: u16) -> Self {
let mut n = self.clone();
n.data.insert(c, value);
return n;
}
fn remove(&self, c: char) -> Self {
if !self.data.contains_key(&c) {
return *self; // <-- error[E0507]: cannot move out of `*self` which is behind a shared reference
}
let mut n = self.clone();
n.data.remove(&c);
return n;
}
}
fn main() {
let s = Something::new();
let s = s.add('a', 1);
let s = s.add('b', 2);
let s = s.remove('a');
// Should return the same instance as c is missing
let s = s.remove('c');
}
but If I change the remove function to return &Self then I can’t return a reference to a variable declared in the function:
fn remove(&self, c: char) -> &Self {
if !self.data.contains_key(&c) {
return self;
}
let mut n = self.clone();
n.data.remove(&c);
return &n; // <--- error[E0515]: cannot return reference to local variable `n`
}
I know I can avoid borrowing something but I don’t want to move ownership:
// <--- self and not &self
fn remove(self, c: char) -> Self {
if !self.data.contains_key(&c) {
return self;
}
let mut n = self.clone();
n.data.remove(&c);
return n;
}
Because then the following will fail:
fn main() {
let s = Something::new();
let s = s.add('a', 1);
let s = s.add('b', 2);
let s1 = s.remove('a');
let s2 = s.remove('a'); // <-- error[E0382]: use of moved value: `s`
}
>Solution :
A &self->Self method will always have to clone. That’s a fundamental thing about ownership.
There are two possible solutions to this, besides the obvious one of "just clone":
-
Make it
&self->&self. Of course, then you won’t be able to modify it, butCowcan solve that:use std::borrow::Cow; fn remove(&self, c: char) -> Cow<'_, Self> { if !self.data.contains_key(&c) { return Cow::Borrowed(self); } let mut n = self.clone(); n.data.remove(&c); Cow::Owned(n) } -
Use a cheaply cloneable data structure that also allows Copy-On-Write, such as
RcorArc:use std::rc::Rc; use std::collections::HashMap; #[derive(Clone)] struct Something { data: Rc<HashMap<char, u16>>, } impl Something { fn new() -> Self { Something { data: Rc::new(HashMap::new()), } } fn get(&self, c: char) -> Option<&u16> { self.data.get(&c) } fn add(&self, c: char, value: u16) -> Self { let mut this = self.clone(); let n = Rc::make_mut(&mut this.data); n.insert(c, value); this } fn remove(&self, c: char) -> Self { let mut this = self.clone(); if !this.data.contains_key(&c) { return this; } let n = Rc::make_mut(&mut this.data); n.remove(&c); this } }