Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to switch variants in the value of a mutable reference to an enum?

Tags:

enums

rust

Is it possible to switch variants in the value of a mutable reference (&mut E<T>) without additional constraints on T, and without resorting to unsafe code?

That is, given an enum:

enum E<T> {
    VariantA(T),
    VariantB(T)
}

What is the correct way of writing this:

let x: E<???> = E::VariantA(??);
change_to_variant_b(&mut x);
assert_eq!(x, E::VariantB(??));
like image 883
Doe Avatar asked Feb 24 '17 06:02

Doe


2 Answers

As an alternative for this particular case, you could consider changing to a struct with a T and a plain enum:

struct Outer<T> {
    val: T,
    kind: Inner,
}

impl<T> Outer<T> {
    fn promote_to_b(&mut self) {
        self.kind.promote_to_b()
    }
}

enum Inner {
    VariantA,
    VariantB,
}

impl Inner {
    fn promote_to_b(&mut self) {
        if let Inner::VariantA = *self {
            *self = Inner::VariantB;
        }
    }
}
like image 130
Shepmaster Avatar answered Nov 18 '22 18:11

Shepmaster


I am going to go on a limb here and say No.


It is possible with just a minor change to the signature though:

fn change_to_variant_b<T>(e: E<T>) -> E<T> {
    match e {
        E::VariantA(t) => E::VariantB(t),
        E::VariantB(t) => E::VariantB(t),
    }
}

It is possible using unsafe:

fn change_to_variant_b<T>(e: &mut E<T>) {
    use std::ptr;

    unsafe {
        match ptr::read(e as *const _) {
            E::VariantA(t) => ptr::write(e as *mut _, E::VariantB(t)),
            E::VariantB(t) => ptr::write(e as *mut _, E::VariantB(t)),
        }
    }
}

It is possible with additional bounds (Default, or Clone):

fn change_to_variant_b<T: Default>(e: &mut E<T>) {
    match std::mem::replace(e, E::VariantA(T::default())) {
        E::VariantA(t) => e = E::VariantB(t),
        E::VariantB(t) => e = E::VariantB(t),
    }
}
like image 8
Matthieu M. Avatar answered Nov 18 '22 17:11

Matthieu M.