How do you replace the value of a mutable variable

2020-04-21 07:44发布

I am working with a LinkedList and I want to remove all elements which do not pass a test. However, I am running into the error cannot move out of borrowed content.

From what I understand, this is because I am working with &mut self, so I do not have the right to invalidate (i.e. move) one of the contained values even for a moment to construct a new list of its values.

In C++/Java, I would simply iterate the list and remove any elements which match a criteria. As there is no remove that I have yet found, I have interpreted it as an iterate, filter, and collect.

The goal is to avoid creating a temporary list, cloning values, and needing take self and return a "new" object. I have constructed an example which produces the same error. Playground.

use std::collections::LinkedList;

#[derive(Debug)]
struct Example {
    list: LinkedList<i8>,
    // Other stuff here
}

impl Example {
    pub fn default() -> Example {
        let mut list = LinkedList::new();
        list.push_back(-5);
        list.push_back(3);
        list.push_back(-1);
        list.push_back(6);
        Example { list }
    }

    // Simmilar idea, but with creating a new list
    pub fn get_positive(&self) -> LinkedList<i8> {
        self.list.iter()
            .filter(|&&x| x > 0)
            .map(|x| x.clone())
            .collect()
    }

    // Now, attempt to filter the elements without cloning anything
    pub fn remove_negative(&mut self) {
        self.list = self.list.into_iter()
            .filter(|&x| x > 0)
            .collect()
    }
}

fn main() {
    let mut e = Example::default();
    println!("{:?}", e.get_positive());
    println!("{:?}", e);
}

In my actual case, I cannot simply consume the wrapping object because it needs to be referenced from different places and contains other important values.

In my research, I found some unsafe code which leads me to question if a safe function could be constructed to perform this action in a similar way to std::mem::replace.

2条回答
迷人小祖宗
2楼-- · 2020-04-21 08:37

If the goal is not clone you may use a reference-counting pointer: the clone method on Rc increments the reference counter.

use std::collections::LinkedList;
use std::rc::Rc;

#[derive(Debug)]
struct Example {
    list: LinkedList<Rc<i8>>,
    // ...
}

impl Example {
    pub fn default() -> Example {
        let mut list = LinkedList::new();
        list.push_back(Rc::new(-5));
        list.push_back(Rc::new(3));
        list.push_back(Rc::new(-1));
        list.push_back(Rc::new(6));
        Example { list }
    }

    // Simmilar idea, but with creating a new list
    pub fn get_positive(&self) -> LinkedList<Rc<i8>> {
        self.list.iter()
            .filter(|&x| x.as_ref() > &0)
            .map(|x| x.clone())
            .collect()
    }

    // Now, attempt to filter the elements without cloning anything
    pub fn remove_negative(&mut self) {
        self.list = self.list.iter()
            .filter(|&x| x.as_ref() > &0)
            .map(|x| x.clone())
            .collect()
    }


}


fn main() {
    let mut e = Example::default();
    e.remove_negative();
    println!("{:?}", e.get_positive());
    println!("{:?}", e);
}
查看更多
对你真心纯属浪费
3楼-- · 2020-04-21 08:42

You can std::mem::swap your field with a temp, and then replace it with your modified list like this. The big downside is the creation of the new LinkedList. I don't know how expensive that is.

pub fn remove_negative(&mut self) {
    let mut temp = LinkedList::new();
    std::mem::swap(&mut temp, &mut self.list);

    self.list = temp.into_iter()
         .filter(|&x| x > 0)
         .collect();
}
查看更多
登录 后发表回答