8

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.

karlicoss
  • 2,501
  • 4
  • 25
  • 29
  • 5
    This is probably just a side effect of the transition from ~ to box. Latest nightly does what you want. – Arjan Sep 12 '14 at 09: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. – Chris Morgan Sep 15 '14 at 20:24

3 Answers3

16

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.

Johannes Maria Frank
  • 2,747
  • 1
  • 29
  • 38
  • It's basically the same as `unsafe { Box::<[T; $len]>::try_from(vec![$val; $len].into_boxed_slice()).unwrap_unchecked() }`, isn't it? – Des Nerger Mar 23 '23 at 01:15
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.

Daniel Robertson
  • 1,354
  • 13
  • 22
  • 4
    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 Jan 26 '15 at 20:54
  • @Shepmaster Thanks for the note. Great catch! – Daniel Robertson Jan 26 '15 at 21:15
-5

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

  • 5
    This does not create an array, it creates a *vector*. In Rust, [arrays](http://doc.rust-lang.org/stable/book/primitive-types.html#arrays) are distinct types from [vectors](http://doc.rust-lang.org/stable/book/vectors.html). Specifically, in an array the *capacity* and *count of items* are both fixed, while a vector has neither fixed. – Shepmaster Jan 24 '16 at 15:49