изменение поля при сопоставлении с ним шаблона

Я сегодня впервые попробовал свои силы в 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 => ()
}

person flying sheep    schedule 29.04.2013    source источник
comment
какую версию ржавчины вы используете?   -  person barjak    schedule 30.04.2013
comment
Язык Rust быстро меняется, синтаксис и семантика еще не исправлены. В частности, я почти уверен, что поведение match недавно изменилось (или изменится, может быть, в 0.7).   -  person barjak    schedule 01.05.2013


Ответы (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}
% 
person pnkfelix    schedule 01.05.2013
comment
круто, спасибо! поэтому в методе нужен изменяемый указатель на себя, что имеет большой смысл! - person flying sheep; 02.05.2013

Без более конкретного примера трудно сказать, решит ли это вашу проблему, но вы можете использовать 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

person pnkfelix    schedule 30.04.2013
comment
Этот ответ, безусловно, помогает мне лучше понять Rust, но я, кажется, плохо выразился: я хочу переключить состояние, а не изменить его значение. извините за путаницу! Я обновляю свой вопрос соответственно. - person flying sheep; 30.04.2013