10

I've tried to use the following code:

fn main() {
    let array = box [1, 2, 3];
}

, in my program, and it results in a compile error: error: obsolete syntax: ~[T] is no longer a type.

AFAIU, there are no dynamic size arrays in Rust (the size has to be known at compile time). However, in my code snippet the array does have static size and should be of type ~[T, ..3] (owned static array of size 3) whereas the compiler says it has the type ~[T]. Is there any deep reason why it isn't possible to get a static sized array allocated on the heap?

P.S. Yeah, I've heard about Vec.

2
  • 5
    This is probably just a side effect of the transition from ~ to box. Latest nightly does what you want. Commented Sep 12, 2014 at 9:40
  • The workaround from that version would have been to rewrite it box () [1, 2, 3] with possibly additional parentheses required around the [1, 2, 3]. But you don’t need to do that now. Commented Sep 15, 2014 at 20:24

3 Answers 3

19

Since I ended up here, others might as well. Rust has moved along and at the point of this answer Rust is at 1.53 for stable and 1.55 for nightly.

Box::new([1, 2, 3]) is the recommended way, and does its job, however there is a catch: The array is created on the stack and then copied over to the heap. This is a documented behaviour of Box:

Move a value from the stack to the heap by creating a Box:

Meaning, it contains a hidden memcopy, and with large array, the heap allocation even fails with a stack overflow.

const X: usize = 10_000_000;
let failing_huge_heap_array = [1; X];

thread 'main' has overflowed its stack
fatal runtime error: stack overflow

There are several workarounds to this as of now (Rust 1.53), the most straightforward is to create a vector and turn the vector into a boxed slice:

const X: usize = 10_000_000;
let huge_heap_array = vec![1; X].into_boxed_slice();

This works, but has two small catches: It looses the type information, what should be Box<[i32; 10000000]> is now Box<[usize]> and additionally takes up 16 bytes on the stack as opposed to an array which only takes 8.

...
println!("{}", mem::size_of_val(&huge_heap_array);

16

Not a huge deal, but it hurts my personal Monk factor.

Upon further research, discarding options that need nightly like the OP box [1, 2, 3] which seems to be coming back with the feature #![feature(box_syntax)] and the arr crate which is nice but also needs nightly, the best solution I found to allocating an array on the heap without the hidden memcopy was a suggestion by Simias

/// A macro similar to `vec![$elem; $size]` which returns a boxed array.
///
/// ```rustc
///     let _: Box<[u8; 1024]> = box_array![0; 1024];
/// ```
macro_rules! box_array {
    ($val:expr ; $len:expr) => {{
        // Use a generic function so that the pointer cast remains type-safe
        fn vec_to_boxed_array<T>(vec: Vec<T>) -> Box<[T; $len]> {
            let boxed_slice = vec.into_boxed_slice();

            let ptr = ::std::boxed::Box::into_raw(boxed_slice) as *mut [T; $len];

            unsafe { Box::from_raw(ptr) }
        }

        vec_to_boxed_array(vec![$val; $len])
    }};
}
const X: usize = 10_000_000;
let huge_heap_array = box_array![1; X];

It does not overflow the stack and only takes up 8 bytes while preserving the type.

It uses unsafe, but limits this to a single line of code. Until the arrival of the box [1;X] syntax, IMHO a clean option.

Sign up to request clarification or add additional context in comments.

2 Comments

It's basically the same as unsafe { Box::<[T; $len]>::try_from(vec![$val; $len].into_boxed_slice()).unwrap_unchecked() }, isn't it?
I believe your answer is incorrect because during casting it assumes that *mut [T] and *mut [T; S] are the same sizes, alas they aren't (the former is 16 bytes, the latter is 8). Therefore what I would do is: let boxed = vec.into_boxed_slice(); let slice = Box::leak(boxed); let pointer = slice.as_mut_ptr() as _; unsafe { Box::from_raw(pointer) }. This way, we get the actual pointer to the slice's buffer and not a pointer to the slice itself, which is exactly what we wanted, as that pointer is 8 bytes in size, the same as *mut [T; S].
5

As far as I know the box expression is experimental. You can use Box::new() with something like the code below to suppress warnings.

fn main() {
    let array1 = Box::new([1, 2, 3]);
    // or even
    let array2: Box<[i32]> = Box::new([1, 2, 3]);
}

Check out the comment by Shepmaster below, as these are different types.

2 Comments

To note, these are slightly different types. array1 is a Box<[_; 3]> - a heap-allocated array of exactly 3 things; where each item is an undetermined integer type. array2 is a Box<[i32]>, a heap-allocated slice of 32-bit signed integers.
@Shepmaster Thanks for the note. Great catch!
-5

Just write like: let mut buffer= vec![0; k]; it makes u8 array with length equals k.

1 Comment

This does not create an array, it creates a vector. In Rust, arrays are distinct types from vectors. Specifically, in an array the capacity and count of items are both fixed, while a vector has neither fixed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.