在模式匹配时修改字段

时间:2013-04-29 23:27:17

标签: pointers pattern-matching rust

今天我第一次尝试使用Rust(写一个XML标记器),自然不明白一切:

我有一个带字段的结构,可以采用枚举值:

enum State { Outside, InATag(~str) }
struct Tokenizer { state: State }

impl Tokenizer中,我希望匹配当前状态,并在某些情况下更改它,但这总是会出现use of moved value错误。

H访问和/或声明状态字段,以便我可以匹配它并在匹配分支中更改其值?


很抱歉这个混乱,我打算更改Tokenizer的状态字段,而不是状态的String字段!

match self.state {
    InATag(name) => self.state = Outside,
    Outside => ()
}

2 个答案:

答案 0 :(得分:4)

如果没有更具体的例子,很难说这是否能解决您的问题,但您可以在匹配模式中使用ref来引用匹配的子结构,并且可以使用{{1使该引用变得可变。

所以,在你的例子中:

ref mut

或者如果您需要匹配Tokenizer状态的其他部分,这也适用:

enum State { Outside, InATag(~str) }
struct Tokenizer { state: State }

fn main() {
    let mut t = Tokenizer { state: InATag(~"foo") };
    match t.state {
         InATag(ref mut _s)  => { *_s = ~"bar"; }
         Outside => { /* impossible */ }
    }
    io::println(fmt!("Hello World: %?", t));
}

请注意,在执行此类代码时,由于别名导致无意中遇到借用检查违规行为非常容易。例如,对于上面第二个示例,将不会编译,这是一个相对较小的更改:

fn main() {
    let mut t = Tokenizer { state: InATag(~"foo") };
    match t {
        Tokenizer { state: InATag(ref mut _s) } => { *_s = ~"bar"; }
        Tokenizer { state: Outside } => { /* impossible */ }
    }
    io::println(fmt!("Hello World: %?", t));
}

导致来自rustc的以下消息:

fn main() {
    let mut t = Tokenizer { state: InATag(~"foo") };
    match &t {
        &Tokenizer { state: InATag(ref mut _s) } => { *_s = ~"bar"; }
        &Tokenizer { state: Outside } => { /* impossible */ }
    }
    io::println(fmt!("Hello World: %?", t));
}

因为您还想要在/tmp/m.rs:7:35: 7:46 error: illegal borrow: creating mutable alias to enum content /tmp/m.rs:7 &Tokenizer { state: InATag(ref mut _s) } => { *_s = ~"bar"; } ^~~~~~~~~~~ error: aborting due to previous error

的内部创建可变别名时不希望使用未完成的借用&t

答案 1 :(得分:2)

好的,有了问题的澄清变体,这是我修改后的答案:

enum State { Outside, InATag(~str) }
struct Tokenizer { state: State }

impl Tokenizer {
    fn toggle(&mut self) {
        match self {
            &Tokenizer { state: InATag(*) } => { self.state = Outside }
            &Tokenizer { state: Outside }   => { self.state = InATag(~"baz") }
        }
    }
}

fn main() {
    let mut t1 = Tokenizer { state: InATag(~"foo") };
    match t1 {
        Tokenizer { state: InATag(*) } => { t1.state = Outside }
        Tokenizer { state: Outside } => { /* impossible */ }
    }
    io::println(fmt!("Hello t1: %?", t1));
    let mut t2 = Tokenizer { state: InATag(~"bar") };
    t2.toggle();
    io::println(fmt!("World t2: %?", t2));
}

我承认,我实际上并没有想到它会像上面那样简单,而且我很容易相信上述代码的微小变化可能导致它开始无法借用 - 检查。但如果没有来自提问者的更充实的例子,很难说上述代码是否适合他的目的。

哦,这是我编译并运行代码时的输出:

% rustc /tmp/m.rs
warning: no debug symbols in executable (-arch x86_64)
% /tmp/m
Hello t1: {state: Outside}
World t2: {state: Outside}
%