简体   繁体   中英

How do I mutate an enum and then return a reference to an enum variant?

I have an enum that can hold either an encoded type ( i32 ) or a decoded type ( String ).

My goal is to write a function that converts the enum to the decoded state, and return a reference, but I can't do it: if I change the content of the enum first, I cannot return a reference.

enum Foo {
    A(i32),
    B(String),
}

use Foo::*;

impl Foo {
    fn get_string(&mut self) -> &str {
        match self {
            A(i) => {
                let s = i.to_string();
                *self = B(s);
                &s
            }
            B(string) => string,
        }
    }
}

I get

error[E0515]: cannot return value referencing local variable `s`
  --> src/lib.rs:10:9
   |
10 | /         match self {
11 | |             A(i) => {
12 | |                 let s = i.to_string();
13 | |                 *self = B(s);
14 | |                 &s
   | |                 -- `s` is borrowed here
15 | |             }
16 | |             B(string) => string,
17 | |         }
   | |_________^ returns a value referencing data owned by the current function

error[E0382]: borrow of moved value: `s`
  --> src/lib.rs:14:17
   |
12 |                 let s = i.to_string();
   |                     - move occurs because `s` has type `String`, which does not implement the `Copy` trait
13 |                 *self = B(s);
   |                           - value moved here
14 |                 &s
   |                 ^^ value borrowed here after move

Is what I want to do possible? If so, how can I do it?

The reference you return needs to point to the data inside Foo::B , not to your local variable s . It's easiest to do this in two steps – first do the conversion if necessary, then return the reference. After the first step it's guaranteed that *self is Foo::B , so we can mark the A branch in the match as unreachable!() .

impl Foo {
    fn get_string(&mut self) -> &str {
        if let A(i) = *self {
            *self = B(i.to_string());
        }
        match *self {
            A(_) => unreachable!(),
            B(ref s) => s,
        }
    }
}

(Note that I changed pattern matching to not use "match ergonomics", since this tends to be less confusing.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM