英文:
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's fairly easy to add elements, and change them if you know the exact position:
```rust
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() < 0 {
panic!("No item to change");
}
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(&mut self, id: u32, new_value: u32) {
let lookup = self.lookup(id);
match lookup {
Some(item) => {
item.value = new_value;
}
None => {
// ...
}
}
}
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 `&` reference
--> redacted/src/lib.rs:170:17
|
169 | Some(item) => {
| ---- consider changing this binding's type to be: `&mut Item`
170 | item.value = new_value;
| ^^^^^^^^^^^^^^^^^^^^^^ `item` is a `&` 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(&mut self, id: u32) -> Option<&mut Item> {
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);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论