Cannot compile enum conversion in const fn

A simple example convert an enum to another enum

enum MyOption<T> {
    Some(T),
    None
}

const fn convert<T>(option: Option<T>) -> MyOption<T> {
    match option {
        Some(t) => MyOption::Some(t),
        None => MyOption::None,
    }
}

But rustc says the input argument will drop at the end of the function, thus cannot run the destructor, so this function cannot be a constant function.

error[E0493]: destructor of `Option<T>` cannot be evaluated at compile-time
  --> src/lib.rs:6:21
   |
6  | const fn convert<T>(option: Option<T>) -> MyOption<T> {
   |                     ^^^^^^ the destructor for this type cannot be evaluated in constant functions
...
11 | }
   | - value is dropped here

For more information about this error, try `rustc --explain E0493`.

Is there anybody who knows why the value dropped at the end of the function? :thinking:
This error doesn't block my work as I don't actually need it to be a const fn, but I wonder why. Thanks.

There is an unstable feature that makes this work:

Apart from that, I can't provide any more insight.

Got it, thanks.
I found another interesting method to achieve this.

enum MyOption<T> {
    Some(T),
    None,
}

#[allow(dead_code)]
const fn convert<T>(mut option: Option<T>) -> MyOption<T> {
    let converted = if option.is_some() {
        // what does `take` and `unwrap` do?
        MyOption::Some(option.take().unwrap())
    } else {
        MyOption::None
    };
    
    // `option` now is sure to be empty
    std::mem::forget(option);
    converted
}

But it works by using take().unwrap() which is a const operation, whose inner uses a special feature.

You don’t need to make use of take(), just unwrap():

const fn convert<T>(option: Option<T>) -> MyOption<T> {
    if option.is_some() {
        MyOption::Some(option.unwrap())
    } else {
        // We know the option is None, so this leaks nothing.
        std::mem::forget(option);
        MyOption::None
    }
}

You could also write it in match form which I think might bring some clarity:

const fn convert<T>(option: Option<T>) -> MyOption<T> {
    match option {
        Some(_) => MyOption::Some(option.unwrap()),
        None => {
            // We know the option is None, so this leaks nothing.
            std::mem::forget(option);
            MyOption::None
        }
    }
}

The fundamental reason this works where a plain match doesn’t is that Option::unwrap() is allowed to use feature(const_precise_live_drops) internally. If that weren’t true, there would be no solution to the problem, because there would be no way to stop having the Option at all.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.