Using const function causes "the destructor for this type cannot be evaluated in constants"

The following code snippet surprisingly brings compiler error despite using fn const to construct a const expression:

#[derive(Debug)]
enum IA<T: 'static> {
    L(&'static [T]),
    A(Box<[T]>),
}

impl<T> IA<T> {
    #[inline]
    const fn new(a: &'static [T]) -> Self {
        IA::L(a)
    }
}

const x: IA<IA<()>> = IA::L(&[IA::L(&[])]); // compiles
const y: IA<IA<()>> = IA::L(&[IA::new(&[])]); // doesn't compile

fn main() {
    println!("x: {x:?}");
    println!("y: {y:?}");
}

Compiler error:

   Compiling t v0.1.0 (/tmp/t)
error[E0493]: destructor of `[IA<()>; 1]` cannot be evaluated at compile-time
  --> src/main.rs:15:30
   |
15 | const y: IA<IA<()>> = IA::L(&[IA::new(&[])]);
   |                              ^^^^^^^^^^^^^^- value is dropped here
   |                              |
   |                              the destructor for this type cannot be evaluated in constants

For more information about this error, try `rustc --explain E0493`.
error: could not compile `t` (bin "t") due to 1 previous error

The goal of IA is to allow constructing constants when it is possible. In this example it doesn't look like using const fn makes any difference to code but in real code the IA is parameterized by more complex type thus using const fn new to construct the instance of the type significantly simplifies the notation.

Is this behavior inevitable or may be some workaround exists?

This issue stymied some of my attempted workarounds.

You could constly create the L variant's innards instead.

1 Like

For some reason, this compiles:

const TEMP: &[IA<()>] = &[IA::new(&[])];
const y: IA<IA<()>> = IA::L(TEMP);

O_o

2 Likes

Ah, so, this does too.

const y: IA<IA<()>> = IA::L(const { &[IA::new(&[])] });

ponders

And so does this.

const y: IA<IA<()>> = IA::L { 0: &[IA::new(&[])] };

Some temporary or[1] const-promotion quirk around the function-call looking L variant constructor maybe?


  1. I think only let gets these ↩︎

This is bizarre

struct Thing(Option<Box<()>>);
impl Thing {
    const fn new() -> Thing {
        Thing(None)
    }
}

struct Wrap(&'static Thing);

// Compiles fine
const X: Wrap = Wrap(&Thing(None));

// Compiles fine
const TEMP: &Thing = &Thing::new();
const Y: Wrap = Wrap(TEMP);

// Fails to compile???
const Y2: Wrap = Wrap(&Thing::new());

My best guess so far is it thinks the outer variant constructor might panic and have to drop the value, which is not constant-promoted without some prodding.

1 Like

This compiles

const y: IA<IA<()>> = IA::L(const { &[ IA::new(&[])] });

Same or related:

1 Like

I think it's not constant-promoting because, for all it knows, new() might panic. See 3027-infallible-promotion - The Rust RFC Book

This compiles?????

const y: IA<IA<()>> = IA::L { 0: &[IA::new(&[])] };

I guess &[IA] doesn't automatically constant promote because IA has a destructor, even though it's possible to create a const one. That part is acting as expected.

Thanks for help @quinedot, @theemathas. I finally used const block const {} to make it work. Yeah, it is strange that some alternatives compile and other not. I also found that splitting expression on few constants work but didn't found "const block" solution. Thank you for finding related issues.