查找矢量中的元素并更改它?

huangapple go评论73阅读模式
英文:

Can you lookup an element of a vector and change it?

问题

Here's the translated code you requested:

假设我有这些结构体(没有复杂的指针,一切都可以复制)

```rust
#[derive(Debug, Copy, Clone)]
struct Item {
    id: u32,
    pub value: u32
}

我可以创建一个结构体来保存这些项的向量:

#[derive(Debug)]
struct Container {
    next_id: u32,
    items: Vec<Item>
}

如果您知道确切的位置,添加元素并更改它们非常容易:

impl Container {
    fn new() -> Container {
        Container {
            next_id: 0,
            items: vec!()
        }
    }

    fn add(&mut self) -> u32 {
        let id = self.next_id;
        self.items.push(Item {
            id: self.next_id,
            value: 0
        });
        self.next_id = self.next_id + 1;
        id
    }

    fn lookup(&mut self, id: u32) -> Option<&Item> {
        self.items.iter().find(|item| item.id == id)
    }

    fn change_first(&mut self, new_value: u32) {
        if self.items.len() < 1 {
            panic!("No item to change");
        }
        self.items[0].value = new_value;
    }
}

这样可以使这样的测试通过:

#[test]
fn can_change_first() {
    let mut c = Container::new();
    assert_eq!(c.add(), 0);
    c.change_first(22);
    assert_eq!(c.lookup(0).unwrap().value, 22);
}

但是,假设我想查找一个元素并找到它。我不想直接暴露items向量,而是提供一个API来实现这一点:

#[test]
fn can_lookup_and_change() {
    let mut c = Container::new();
    assert_eq!(c.add(), 0);
    c.change_at(0, 22);
    assert_eq!(c.lookup(0).unwrap().value, 22);
}

change_at的天真实现会是:

fn change_at(&mut self, id: u32, new_value: u32) {
    let lookup = self.lookup(id);
    match lookup {
        Some(item) => {
            item.value = new_value;
        }
        None => {
            // ...
        }
    }
}

然而,编译器不接受这样做,会出现错误:

   编译web v0.1.0/home/phtrivier/perso/prj/wirth/dom-web
error[E0594]: 不能赋值给`item.value`,因为它在`&`引用后面
   --> redacted/src/lib.rs:170:17
    |
169 |             Some(item) => {
    |                  ---- 考虑将此绑定的类型更改为:`&mut Item`
170 |                 item.value = new_value;
    |                 ^^^^^^^^^^^^^^^^^^^^^^ `item`是一个`&`引用,所以它引用的数据不能被写入

有关此错误的更多信息,请尝试运行`rustc --explain E0594`。

我不清楚错误是在lookup方面(是否有不同的查找方式可以使它们变为mut?)还是在change_at方面(这是模式匹配的正确方式吗?)。


<details>
<summary>英文:</summary>

Assuming I have those structs (no fancy pointer, everything is copyable)

#[derive(Debug, Copy, Clone)]
struct Item {
id: u32,
pub value: u32
}


I can create a struct to hold a vec of such items: 

#[derive(Debug)]
struct Container {
next_id: u32,
items: Vec<Item>
}


It&#39;s fairly easy to add elements, and change them if you know the exact position: 

```rust
impl Container {
    fn new() -&gt; Container {
        Container {
            next_id: 0,
            items: vec!()
        }
    }

    fn add(&amp;mut self) -&gt; u32 {
        let id = self.next_id;
        self.items.push(Item {
            id: self.next_id,
            value: 0
        });
        self.next_id = self.next_id + 1;
        id
    }

    fn lookup(&amp;mut self, id: u32) -&gt; Option&lt;&amp;Item&gt; {
        self.items.iter().find(|item| item.id == id)
    }

    fn change_first(&amp;mut self, new_value: u32) {
        if self.items.len() &lt; 0 {
            panic!(&quot;No item to change&quot;);
        }
        self.items[0].value = new_value;
    }
}

This makes such a test pass:

    #[test]
    fn can_change_first() {
        let mut c = Container::new();
        assert_eq!(c.add(), 0);
        c.change_first(22);
        assert_eq!(c.lookup(0).unwrap().value, 22);
    }

However, suppose I want to lookup and element and find it. I don't want to expose the
items vec directly, but propose an API to do so:

 #[test]
    fn can_lookup_and_change() {
        let mut c = Container::new();
        assert_eq!(c.add(), 0);
        c.change_at(0, 22);
        assert_eq!(c.lookup(0).unwrap().value, 22);
    }

The naïve implementation of change_at would be:

    fn change_at(&amp;mut self, id: u32, new_value: u32) {
        let lookup = self.lookup(id);
        match lookup {
            Some(item) =&gt; {
                item.value = new_value;
            }
            None =&gt; {
                // ...
            }
        }
    }

However, the compiler does not accept that, giving the error:

   Compiling web v0.1.0 (/home/phtrivier/perso/prj/wirth/dom-web)
error[E0594]: cannot assign to `item.value`, which is behind a `&amp;` reference
   --&gt; redacted/src/lib.rs:170:17
    |
169 |             Some(item) =&gt; {
    |                  ---- consider changing this binding&#39;s type to be: `&amp;mut Item`
170 |                 item.value = new_value;
    |                 ^^^^^^^^^^^^^^^^^^^^^^ `item` is a `&amp;` reference, so the data it refers to cannot be written

For more information about this error, try `rustc --explain E0594`.

I don't understand if the error is on the lookup side (is there a different way to lookup up items that will make them mut ?) or on the change_at side (is this the right way to pattern match ?)

答案1

得分: 5

你可以添加一个lookup_mut方法。这将用iter_mut替换iter

fn lookup_mut(&mut self, id: u32) -> Option<&mut Item> {
    self.items.iter_mut().find(|item| item.id == id)
}

然后在需要可变引用时使用它。

let lookup = self.lookup_mut(id);
英文:

You can add a lookup_mut method. This would replace iter with iter_mut.

fn lookup_mut(&amp;mut self, id: u32) -&gt; Option&lt;&amp;mut Item&gt; {
    self.items.iter_mut().find(|item| item.id == id)
}

And then use that when you need a mutable reference.

let lookup = self.lookup_mut(id);

huangapple
  • 本文由 发表于 2023年5月14日 08:29:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76245369.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定