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

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

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

问题

Here's the translated code you requested:

  1. 假设我有这些结构体(没有复杂的指针,一切都可以复制)
  2. ```rust
  3. #[derive(Debug, Copy, Clone)]
  4. struct Item {
  5. id: u32,
  6. pub value: u32
  7. }

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

  1. #[derive(Debug)]
  2. struct Container {
  3. next_id: u32,
  4. items: Vec<Item>
  5. }

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

  1. impl Container {
  2. fn new() -> Container {
  3. Container {
  4. next_id: 0,
  5. items: vec!()
  6. }
  7. }
  8. fn add(&mut self) -> u32 {
  9. let id = self.next_id;
  10. self.items.push(Item {
  11. id: self.next_id,
  12. value: 0
  13. });
  14. self.next_id = self.next_id + 1;
  15. id
  16. }
  17. fn lookup(&mut self, id: u32) -> Option<&Item> {
  18. self.items.iter().find(|item| item.id == id)
  19. }
  20. fn change_first(&mut self, new_value: u32) {
  21. if self.items.len() < 1 {
  22. panic!("No item to change");
  23. }
  24. self.items[0].value = new_value;
  25. }
  26. }

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

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

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

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

change_at的天真实现会是:

  1. fn change_at(&mut self, id: u32, new_value: u32) {
  2. let lookup = self.lookup(id);
  3. match lookup {
  4. Some(item) => {
  5. item.value = new_value;
  6. }
  7. None => {
  8. // ...
  9. }
  10. }
  11. }

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

  1. 编译web v0.1.0/home/phtrivier/perso/prj/wirth/dom-web
  2. error[E0594]: 不能赋值给`item.value`,因为它在`&`引用后面
  3. --> redacted/src/lib.rs:170:17
  4. |
  5. 169 | Some(item) => {
  6. | ---- 考虑将此绑定的类型更改为:`&mut Item`
  7. 170 | item.value = new_value;
  8. | ^^^^^^^^^^^^^^^^^^^^^^ `item`是一个`&`引用,所以它引用的数据不能被写入
  9. 有关此错误的更多信息,请尝试运行`rustc --explain E0594`

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

  1. <details>
  2. <summary>英文:</summary>
  3. Assuming I have those structs (no fancy pointer, everything is copyable)

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

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

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

  1. It&#39;s fairly easy to add elements, and change them if you know the exact position:
  2. ```rust
  3. impl Container {
  4. fn new() -&gt; Container {
  5. Container {
  6. next_id: 0,
  7. items: vec!()
  8. }
  9. }
  10. fn add(&amp;mut self) -&gt; u32 {
  11. let id = self.next_id;
  12. self.items.push(Item {
  13. id: self.next_id,
  14. value: 0
  15. });
  16. self.next_id = self.next_id + 1;
  17. id
  18. }
  19. fn lookup(&amp;mut self, id: u32) -&gt; Option&lt;&amp;Item&gt; {
  20. self.items.iter().find(|item| item.id == id)
  21. }
  22. fn change_first(&amp;mut self, new_value: u32) {
  23. if self.items.len() &lt; 0 {
  24. panic!(&quot;No item to change&quot;);
  25. }
  26. self.items[0].value = new_value;
  27. }
  28. }

This makes such a test pass:

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

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:

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

The naïve implementation of change_at would be:

  1. fn change_at(&amp;mut self, id: u32, new_value: u32) {
  2. let lookup = self.lookup(id);
  3. match lookup {
  4. Some(item) =&gt; {
  5. item.value = new_value;
  6. }
  7. None =&gt; {
  8. // ...
  9. }
  10. }
  11. }

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

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

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

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

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

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

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

And then use that when you need a mutable reference.

  1. 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:

确定