What's the idiomatic way to convert between `enum` variants?

Say I have

struct Data;

enum State {
  First(Data),
  Second(Data, Data),
}

And I want to add a method to it converting First to Second:

impl State {
  fn convert(&mut self, extra_data: Data) {
    ...
  }
}

If Data is not constructable in impl State scope, this is impossible in safe Rust (right?).

And I can see two options:

  1. Add a placeholder variant to State:
enum State {
  Empty,
  First(Data),
  Second(Data, Data)
}

impl State {
  fn convert(&mut self, extra_data: Data) {
    let mut this = State::Empty;
    std::mem::swap(&mut this, self);
    if let State::First(data) = this {
      *self = State::Second(data, extra_data);
    } else {
      std::mem::swap(&mut this, self);
    }
  }

But this leaks implementation detail and implementation is unnecessarily long and error-prone.

  1. Change method signature to consuming self:
impl State {
  fn convert(self, extra_data: Data) -> Self {
    if let State::First(data) = self {
      State::Second(data, extra_data)
    } else {
      self
    }
  }
}

But this is contagious in the sense that if a outer struct holds State and wants to call convert in its own method, that method must comsume self too.

I'm wondering if there's a better way to implement this pattern? Thanks!

You could do it with the take_mut crate.

2 Likes

Here's an article about a more general pattern (that includes this case).

https://smallcultfollowing.com/babysteps/blog/2018/11/10/after-nll-moving-from-borrowed-data-and-the-sentinel-pattern/

1 Like