#Initializing an array of non-Copy items in cons context

12 messages · Page 1 of 1 (latest)

stoic bear
#

Hi!

I am trying to initialize an array in const context with non-Copy items.
This compiles when the array has a length of 1 and fails for other lengths (including 0).
When the length is 0, the compiler complains that the item's destructor cannot be evaluated at compile time.
When the length is greater than 1, the compiler wants the item to be copyable.

Here a simplified example:

#[derive(Debug, Default, Clone)]
struct SomeStruct {
    field: std::borrow::Cow<'static, str>,
}
impl SomeStruct {
    const fn new() -> Self {
        Self { field: std::borrow::Cow::Borrowed("") }
    }
}

const DATA0: [SomeStruct; 1] = [SomeStruct::new(); 0];
const DATA1: [SomeStruct; 1] = [SomeStruct::new(); 1]; // This one compiles!
const DATA2: [SomeStruct; 2] = [SomeStruct::new(); 2];

I understand why the compiler rejects for length greater than 1. Not sure why it rejects the 0-length because it could simply not call SomeStruct::new() and avoids the destructor call issue.

Do you know some way of getting around these limitations?
II thought about using a combination of zero initialisation, ManuallyDrop and transmute_copy. However, this is quite complex, and I am afraid it could create memory leaks (although this is not so problematic because it is done at compile time).

Note that my real code is a bit more complex: The array length is computed using a const fn.

granite ermine
#

[const { SomeStruct::new() }; 2]

#

The ; 0] one doesn't compile because rust has to create a SomeStruct and then immediately drop the SomeStruct. And it isn't smart enough to know that dropping this SomeStruct doesn't require dropping a String.

stoic bear
#

Oh actually it compiles!
In fact my example is a bit more complex, it is more like:

const DATA2: [(usize, SomeStruct); COUNT] = [(0, SomeStruct::new()); COUNT];

I previously tried this:

const DATA2: [(usize, SomeStruct); COUNT] = [(0, const {  SomeStruct::new() }); COUNT];

And it didn't work.
I've just realized that I had to wrap the entire initializer:

const DATA2: [(usize, SomeStruct); COUNT] = [const { (0,   SomeStruct::new() }); COUNT];

And this works!
I was so close.
It is strange that Rustc requires all this const {} ceremony. We are already in const context...

Anyway. Thanks for your help!

granite ermine
#

So rust wants you to make an explicit choice to request a const promotion here

#

?play

#[derive(Copy, Clone)]
struct Thing;

const fn make_thing() -> Thing {
    panic!();
}

fn main() {
    let _x = [make_thing(); 2];
}
finite nebulaBOT
#
thread 'main' panicked at src/main.rs:5:5:
explicit panic
note: run with ​`RUST_BACKTRACE=1​` environment variable to display a backtrace```
granite ermine
#

This panics at run time

#

?play

#[derive(Copy, Clone)]
struct Thing;

const fn make_thing() -> Thing {
    panic!();
}

fn main() {
    let _x = [const { make_thing() }; 2];
}
finite nebulaBOT
#
error[E0080]: evaluation panicked: explicit panic
 --> src/main.rs:9:23
  |
9 |     let _x = [const { make_thing() }; 2];
  |                       ^^^^^^^^^^^^ evaluation of `main::{constant#0}` failed inside this call
  |
note: inside `make_thing`
 --> src/main.rs:5:5
  |
5 |     panic!();
  |     ^^^^^^^^ the failure occurred here

note: erroneous constant encountered
 --> src/main.rs:9:15
  |
9 |     let _x = [const { make_thing() }; 2];
  |               ^^^^^^^^^^^^^^^^^^^^^^

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