rambi
rambi

Reputation: 1259

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?

Upvotes: 2

Views: 1056

Answers (1)

Sven Marnach
Sven Marnach

Reputation: 601659

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.)

Upvotes: 3

Related Questions