在对字段进行模式匹配时修改字段

fly*_*eep 6 pointers pattern-matching rust

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

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

enum State { Outside, InATag(~str) }
struct Tokenizer { state: State }
Run Code Online (Sandbox Code Playgroud)

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

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


抱歉造成混淆,我的意思是更改 Tokenizer 的 state 字段,而不是 state 的 String 字段!

match self.state {
    InATag(name) => self.state = Outside,
    Outside => ()
}
Run Code Online (Sandbox Code Playgroud)

pnk*_*lix 5

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

所以,在你的例子中:

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));
}
Run Code Online (Sandbox Code Playgroud)

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

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));
}
Run Code Online (Sandbox Code Playgroud)

请注意,在执行此类代码时,很容易因别名而无意中遇到借用检查违规。例如,这里对上面的第二个示例进行了相对较小的更改,该更改无法编译:

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));
}
Run Code Online (Sandbox Code Playgroud)

导致来自 rustc 的以下消息:

/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
Run Code Online (Sandbox Code Playgroud)

因为你不想要一个未偿还的借用,&t同时你也在创建内部的可变别名t


pnk*_*lix 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));
}
Run Code Online (Sandbox Code Playgroud)

我承认,我实际上没想到它会像上面那样简单,而且我很容易相信对上面代码的微小更改可能会导致它开始无法借用检查。但如果没有提问者提供更具体的例子,很难判断上面的代码是否适合他的目的。

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

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