6

今天第一次尝试 Rust(写一个 XML 分词器),自然不是什么都懂:

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

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

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

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


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

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

2 回答 2

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));
}

或者如果您需要匹配 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));
}

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

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 的以下消息:

/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,同时你也在为t

于 2013-04-30T12:24:04.623 回答
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}
% 
于 2013-05-01T12:02:12.187 回答