无法作为可变借用,因为它在一个`&`引用后面。

30
为了更好地了解Rust,我正在构建一个简单的文本编辑器,并拥有以下结构体:

为了更好地了解Rust,我正在构建一个简单的文本编辑器,并拥有以下结构体:

struct File {
    rows: Vec<Row>,
    filename: Option<String>
}

impl File {
    fn row(&self, index: u16) -> &Row{
        &self.rows[index as usize]
    }

}

struct Row {
    string: String,
}

struct EditorState {
    file: File,
}

正如您所看到的,我正在使用一个结构体来保存编辑器的状态,该结构体引用包含若干行的文件,每个行都包含一个字符串(这些结构体还有更多字段,但我已经移除了与问题无关的字段)。

现在我想让我的行可编辑,因此添加了以下内容:

impl Row {
    fn insert(&mut self, at: u16, c: char) {
        let at = at as usize;
        if at >= self.string.len() {
            self.string.push(c);
        } else {
            self.string.insert(at, c)
        }
    }
}

这是我尝试更新行的方式:

//In the actual functon, I am capturing the keypress,
//get the correct row from the state and pass it and the pressed
// char to row.insert
fn update_row(mut state: &mut EditorState)  {
let row = &state.file.row(0);
row.insert(0, 'a');

}

这个无法编译:

error[E0596]: cannot borrow `*row` as mutable, as it is behind a `&` reference

从这个错误中,我可以看出问题在于 Row 应该是可变的,以便我可以编辑它(这很合理,因为我正在改变它的字符串)。 我无法弄清楚 a)如何能够在此处改变字符串,以及 b)如何在不使 row 总是返回可变引用的情况下实现此目标,因为在所有其他情况下,我都是调用 row 来读取一行,而不是写入。


1
@SvenMarnach 这会给你一个 &mut &Row,这并没有真正帮助到你。 - fjh
5
不,撤销那个。row()函数只返回一个只读引用,所以你不能使用该引用来修改行。你需要添加一个row_mut()函数来返回可变引用。 - Sven Marnach
3个回答

8

这里是更符合惯用习语的File实现:

impl File {
    fn row(&self, index: usize) -> Option<&Row> {
        self.rows.get(index)
    }

    fn row_mut(&mut self, index: usize) -> Option<&mut Row> {
        self.rows.get_mut(index)
    }
}

注意事项:

  • 如果index越界,你的实现会导致panic(程序错误)。处理这种情况的惯用方式是返回Option,而getget_mut方法可以让你免费使用。
  • 在Vec中,使用u16并没有多大意义。因为Vec是使用usize进行索引的。在这里使用u16是任意的,除非你真的想提供硬编码限制。在这种情况下,我不会依赖于类型的最大值,但会使用一个常量来使意图更加清晰。

感谢您 a) 回答问题,b) 指向 getget_mut,以及 c) 强调 u16 问题,这真的没有太多意义。 - Philipp

4
你想要的是不可能实现的。你必须编写两个函数(注意我用usize代替了u16 - 没有理由限制每行字符数为65536):
fn row(&self, index: usize) -> &Row {
    &self.rows[index]
}

fn row_mut(&mut self, index: usize) -> &mut Row {
    &mut self.rows[index]
}

请注意,这是Rust代码中的常见模式。例如,Vecget(idx)get_mut(idx)方法。

0

文件封装了行,因此不要暴露行。
相反,在文件中实现方法以对封装的行执行操作。

一般来说,程序应该具有结构性,如下所示:

object.do_something() 

在你的情况下:

file.do_something_with_row(x)  

替代

let mut row = file.row_mut()
// do something with row here, rows leaking all over the place.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接