Rust Вернуть себя, если не изменено, или новое «Я», если изменилось (копирование при записи)

У меня есть структура с функцией remove, и я хочу, чтобы функция remove возвращала новый экземпляр, если он действительно удален, или ту же ссылку.

Но я получаю Cannot move, когда пытаюсь return *self

Является ли это возможным?

Судя по ошибкам, я так не думаю, но тогда как Cow возможно.

Код:

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');
}

но если я изменю функцию remove на возврат &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;  // <--- error[E0515]: cannot return reference to local variable `n`
    }

Я знаю, что могу избежать займов something, но я не хочу передавать право собственности:

    // <--- 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;
    }

Потому что тогда следующее не удастся:

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`
}

Существует встроенный тип копирования при записи (Корова), который должен подойти для вашего случая.

Filipe Rodrigues 08.08.2024 20:36

Разве я не могу реализовать это сам?

Raz Luvaton 08.08.2024 20:38
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
2
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Метод &self->Self всегда должен клонировать. Это фундаментальная вещь о собственности.

Есть два возможных решения этой проблемы, помимо очевидного «просто клонировать»:

  • Сделайте это &self->&self. Конечно, тогда вы не сможете его модифицировать, но Корова может это решить:

    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)
    }
    
  • Используйте дешевую клонируемую структуру данных, которая также допускает копирование при записи, например Rc или Arc:

    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
        }
    }
    

Можно также использовать обычный Cow или вместо возврата «я или новое я» вернуть Some в случае новой версии и None в противном случае и позволить вызывающему абоненту разобраться с этим.

Masklinn 09.08.2024 07:37

Другие вопросы по теме