0

I am trying to implement Iterator on an enum, such that it switches between variants on each .next() call. I want to reuse some data which is common to each variant, by assigning it to the newly-assigned enum value.

In the following example, I would like to switch the variant between E::A and E::B on each method call, and reuse the similar string value held within the enum:

enum E {
    A(String),
    B(String)
}

impl Iterator for E {
    /* ... */

    fn next(&mut self) -> /* ... */ {
        *self = match *self {
            E::A(ref s) => E::B(*s),
            E::B(ref s) => E::A(*s),
        };

        /*...*/
    }
}

But I get an error about moving out a member of borrowed self:

error: cannot move out of borrowed content [E0507]
main.rs:11       E::A(ref s) => E::B(*s),

Since self is being atomically replaced, the moving-out of its content should be logically sound, but the borrow checker does not detect this.

Currently I am .clone()ing the field I wish to move instead, but this is wasteful.

I have considered the use of mem::swap, which allows atomic swaps of borrowed content; but it looks as though this only works if the two swapped locations exist simultaneously.

Is what I want possible, without the use of unsafe?

Toby Dimmick
  • 21
  • 2
  • 5
  • FWIW, the *discriminant* is the internal integer that uniquely identifies an enum's *variant*. – Shepmaster Nov 02 '17 at 21:36
  • *the moving-out of its content should be logically sound* — it is not in the presence of destructors, such as the one implemented by `String`. – Shepmaster Nov 02 '17 at 21:36
  • @Shepmaster: _not in the presence of destructors_ - Where is the destructor called in this situation? AFAIU the string is simply moved to a new owner (and when `self` is moved rather than borrowed, this is allowed). Edit: do you mean the necessity of a destructor in case of a `panic`, as you mention in [this answer](https://stackoverflow.com/a/36557734/691695)? – Toby Dimmick Nov 02 '17 at 21:54
  • Indeed! The compiler has no way of notifying the owner of the referred-to item that "Hey, remember the `String` that you used to store? Yeah, that's not there anymore, so don't try to free it!" If you want that ability, that's where `Option` would come in. As discussed in the linked duplicate, `String::new` and `Vec::new` (and most of Rust's heavily-used data structures) don't have dynamic allocation for an "empty" collection, so they are pretty cheap to swap in as a placeholder. – Shepmaster Nov 02 '17 at 22:23
  • Yes, it's possible. Use mem::replace. There is a recipe over here https://github.com/rust-unofficial/patterns/blob/master/idioms/mem-replace.md for doing this. – hwiechers Nov 03 '17 at 01:45

0 Answers0