modifying a field while pattern matching on it

梦想的初衷 提交于 2019-12-10 06:45:22

问题


I tried my hands at Rust for the first time today (writing a XML tokenizer), and naturally don’t understand everything:

I have a struct with field that can take an enum value:

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

In a impl Tokenizer, I want to match on the current state, and change it in some cases, however this always gives a use of moved value error.

H to access and/or declare the state field so that I can match on it and change its value inside a match branch?


Sorry for the confusion, I meant to change the Tokenizer’s state field, not the state’s String field!

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

回答1:


Okay, with the clarified variant of the question, here is my revised answer:

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

I admit, I actually did not expect it to be as easy as the above, and I can easily believe that small changes to the above code could cause it to start failing to borrow-check. But without a more fleshed out example from the asker, it is hard to tell whether the above code would suit his purposes or not.

Oh, here's the output when I compile and run the code:

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



回答2:


Without a more concrete example, it is hard to tell whether this would solve your problem, but you can use ref within a match pattern to make a reference to the matched substructure, and you can use ref mut to make that reference mutable.

So, in your example:

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

or if you need to match other parts of the Tokenizer state, this works too:

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

Note that when doing this sort of code, it can be pretty easy to inadvertently run into borrow-check violations due to aliasing. For example, here is a relatively small change to the second example above that won't compile:

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

causes the following message from 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

because you do not want an outstanding borrow &t while you are also creating mutable aliases to the internals of t



来源:https://stackoverflow.com/questions/16289880/modifying-a-field-while-pattern-matching-on-it

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!