Как мне изменить в совпадении, которое занимает неизменное значение? - PullRequest
0 голосов
/ 18 марта 2020

Я могу понять концепции заимствования / владения в Rust, но я не знаю, как обойти этот случай:

use std::collections::{HashMap, HashSet};

struct Val {
    t: HashMap<u16, u16>,
    l: HashSet<u16>,
}

impl Val {
    fn new() -> Val {
        Val {
            t: HashMap::new(),
            l: HashSet::new(),
        }
    }

    fn set(&mut self, k: u16, v: u16) {
        self.t.insert(k, v);
        self.l.insert(v);
    }

    fn remove(&mut self, v: &u16) -> bool {
        self.l.remove(v)
    }

    fn do_work(&mut self, v: u16) -> bool {
        match self.t.get(&v) {
            None => false,
            Some(r) => self.remove(r),
        }
    }
}

fn main() {
    let mut v = Val::new();

    v.set(123, 100);
    v.set(100, 1234);

    println!("Size before: {}", v.l.len());
    println!("Work: {}", v.do_work(123));
    println!("Size after: {}", v.l.len());
}

детская площадка

Компилятор имеет ошибка:

error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
  --> src/main.rs:28:24
   |
26 |         match self.t.get(&v) {
   |               ------ immutable borrow occurs here
27 |             None => false,
28 |             Some(r) => self.remove(r),
   |                        ^^^^^------^^^
   |                        |    |
   |                        |    immutable borrow later used by call
   |                        mutable borrow occurs here

Я не понимаю, почему я не могу мутировать в руке спички, когда я делал get (чтение значения) раньше; self.t.get заканчивается, когда начинается мутация с помощью remove.

Это из-за объема результата (Option<&u16>), возвращаемого get? Это правда, что время жизни результата имеет область видимости внутри выражения соответствия, но этот шаблон дизайна используется очень часто (мутировать в выражении соответствия).

Как обойти ошибку?

Ответы [ 3 ]

2 голосов
/ 18 марта 2020

Объявление функции HashMap::<K,V>::get() немного упрощено:

pub fn get<'s>(&'s self, k: &K) -> Option<&'s V>

Это означает, что он возвращает необязательную ссылку на содержащееся значение, а не само значение. Поскольку возвращенная ссылка указывает на значение внутри карты, она фактически заимствует карту, то есть вы не можете изменить карту, пока эта ссылка существует. Это ограничение защищает вас, что произойдет, если вы удалите это значение, пока ссылка еще жива?

Итак, когда вы пишете:

match self.t.get(&v) {
    None => false,
    //r: &u16
    Some(r) => self.remove(r)
}

захваченный r имеет введите &u16, а его время жизни равно self.t, то есть оно заимствует его. Таким образом, вы не можете получить изменяемую ссылку на self, которая необходима для вызова remove.

Самое простое решение вашей проблемы - clone() решает каждую жизненную проблему шаблон. Так как ваши значения имеют тип u16, то есть Copy, это на самом деле тривиально:

match self.t.get(&v) {
    None => false,
    //r: u16
    Some(&r) => self.remove(&r)
}

Теперь r на самом деле имеет тип u16, поэтому он ничего не заимствует, и вы можете изменять self по желанию.

Если ваши типы ключей / значений не были Copy, вы можете попробовать и clone их, если вы готовы за это заплатить. Если нет, есть еще один вариант, так как ваша функция remove() не изменяет HashMap, а не связана HashSet. Вы все еще можете изменить этот набор, если вы позаботитесь о том, чтобы не заимствовать self:

    fn remove2(v: &u16, l: &mut HashSet<u16>) -> bool {
        l.remove(v)
    }
    fn do_work(&mut self, v: u16) -> bool {
        match self.t.get(&v) {
            None => false,
            //selt.t is borrowed, now we mut-borrow self.l, no problem
            Some(r) => Self::remove2(r, &mut self.l)
        }
    }
0 голосов
/ 18 марта 2020

Кажется, что следующее решение хорошо для примитивных типов, таких как здесь u16. Для других типов владение перемещено.

use std::collections::HashMap;

struct Val {
    t: HashMap<u16, u16>,
}

impl Val {
    fn new() -> Val {
        Val { t: HashMap::new() }
    }

    fn set(&mut self, k: u16, v: u16) {
        self.t.insert(k, v);
    }

    fn remove(&mut self, v: &u16) -> bool {
        match self.t.remove(v) {
            None => false,
            _ => true,
        }
    }

    fn do_work(&mut self, v: u16) -> bool {
        match self.t.get(&v) {
            None => false,
            Some(&v) => self.remove(&v)
        }
    }
}

fn main() {
    let mut v = Val::new();

    v.set(123, 100);
    v.set(100, 1234);

    println!("Size before: {}", v.t.len());
    println!("Work: {}", v.do_work(123));
    println!("Size after: {}", v.t.len());
}

Для других типов мы должны клонировать значение:

use std::collections::{HashMap, HashSet};

#[derive(Debug)]
struct Val {
    t: HashMap<String, String>,
    l: HashSet<String>
}

impl Val {
    fn new() -> Val {
        Val { t: HashMap::new(), l: HashSet::new() }
    }

    fn set(&mut self, k: String, v: String) {
        self.l.insert(v.clone());
        self.t.insert(k, v);
    }

    fn remove(&mut self, v: &String) -> bool {
        self.l.remove(v)
    }

    fn do_work(&mut self, i: &String) -> bool {
        match self.t.get(i) {
            None => false,
            Some(v) => {
                let x = v.clone();

                self.remove(&x)
            }
        }
    }

    fn do_task(&mut self, i: &String) -> bool {
        match self.t.get(i) {
            None => false,
            Some(v) => self.l.insert(v.clone())
        }
    }
}

fn main() {
    let mut v = Val::new();

    v.set("AA".to_string(), "BB".to_string());
    v.set("BB".to_string(), "CC".to_string());

    println!("Start: {:#?}", v);
    println!("Size before: {}", v.l.len());
    println!("Work: {}", v.do_work(&"AA".to_string()));
    println!("Size after: {}", v.l.len());
    println!("After: {:#?}", v);
    println!("Task [Exist]: {}", v.do_task(&"BB".to_string()));
    println!("Task [New]: {}", v.do_task(&"AA".to_string()));
    println!("End: {:#?}", v);
}

Но я бы хотел решение, которое не имеет распределения

0 голосов
/ 18 марта 2020

Вы пытаетесь удалить значение из HashMap, используя value, который вы получаете, а не key.

Изменена только строка 26 Some(_) => self.remove(&v)

Это будет работать:

use std::collections::HashMap;

struct Val {
    t: HashMap<u16, u16>
}

impl Val {
    fn new() -> Val {
        Val { t: HashMap::new() }
    }

    fn set(&mut self, k: u16, v: u16) {
        self.t.insert(k, v);
    }

    fn remove(&mut self, v: &u16) -> bool {
        match self.t.remove(v) {
            None => false,
            _ => true,
        }
    }

    fn do_work(&mut self, v: u16) -> bool {
        match self.t.get(&v) {
            None => false,
            Some(_) => self.remove(&v)
        }
    }
}

fn main() {
    let mut v = Val::new();

    v.set(123, 100);
    v.set(1100, 1234);

    println!("Size before: {}", v.t.len());
    println!("Work: {}", v.do_work(123));
    println!("Size after: {}", v.t.len());
}

play.rust

...