78

I want to create array like this:

let arr = [0; length];

Where length is a usize. But I get this error

E0307
The length of an array is part of its type. For this reason, this length 
must be a compile-time constant.

Is it possible to create array with dynamic length? I want an array, not a Vec.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Dima Kudosh
  • 7,126
  • 4
  • 36
  • 46
  • 4
    I don't think that's possible. Why do you not want to use a Vec? – fjh Jan 08 '16 at 19:43
  • 8
    Maybe you should describe which qualities of a `Vec` you think are lacking that an array satisfies. – Brian Cain Jan 08 '16 at 19:47
  • Or maybe use `Slice`, it is similar to `Array` but size of a `Slice` does not have to be known at compile time. – Akavall Jan 08 '16 at 20:58
  • I have some functions that accepts references to array, so i need array for it. – Dima Kudosh Jan 08 '16 at 21:00
  • 7
    @DimaKudosh, are you sure they accept references to an array, not slices (e.g. `&[u32]`)? If they are in fact slices, then `Vec` is freely convertible to `&[T]`, usually with just `&`: `let v: Vec = ...; let s: &[u32] = &v`. And if they do in fact accept references to an array, then how dynamically sized arrays would help you? Rust does not have type-level integers, so the size of the array would be necessarily constant in this case. – Vladimir Matveev Jan 08 '16 at 21:10
  • @Vladimir Matveev,I create trait for reading u16, u32 and etc from stream and try to implement it for File struct. But I don't want to implement many similar methods, and I try to create one function and call it in this methods with different parameters. Firstly I try to pass length of buffer to it and read this buffer from file and then convert, but it's impossible to create dynamic array. Then I try to create Vec, I convert to integer using transmute and it's panic because Vec hasn't fixed length. Then I pass array but it's impossible to convert [u8;2] to [u8].Maybe you know better solution? – Dima Kudosh Jan 09 '16 at 09:44
  • @Vladimir Matveev,or better to make this function using adding and shifts instead of transmute? – Dima Kudosh Jan 09 '16 at 09:50
  • Of course you should not use transmutes for this. This is not portable. If you need to read binary numbers from a byte stream I suggest you to use [byteorder](https://crates.io/crates/byteorder) crate which does exactly that in a correct way. – Vladimir Matveev Jan 09 '16 at 12:36
  • @Vladimir Matveev, Thank you – Dima Kudosh Jan 09 '16 at 12:43
  • I think you might be looking for [tinyvec](https://docs.rs/tinyvec/latest/tinyvec/) – SColvin Jun 21 '22 at 13:43

3 Answers3

95

Is it possible to create array with dynamic length?

No. By definition, arrays have a length defined at compile time. A variable (because it can vary) is not known at compile time. The compiler would not know how much space to allocate on the stack to provide storage for the array.

You will need to use a Vec:

let arr = vec![0; length];

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Is [`Vec::into_boxed_slice`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_boxed_slice) an option then? How does it compare to a plain Vec or an array? – iago-lito Jun 23 '19 at 21:31
  • 1
    @iago-lito does [What is the use of into_boxed_slice() methods?](https://stackoverflow.com/q/39615666/155423) and [Performance comparison of a Vec and a boxed slice](https://stackoverflow.com/q/33704592/155423) answer your question? – Shepmaster Jun 23 '19 at 21:32
15

This should be possible after variable length arrays (VLA) are implemented.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
railgun
  • 973
  • 1
  • 10
  • 16
  • 1
    I really do hope they implement this. I want to ditch C++ with Rust and I will only trust them if they choose not do same mistakes C++ did. C++ didnt implement designated initializers with random "reasons" VLAs are very similar. They are cornerstones of performance computing both because it helps prevent cache misses which is also very important helper of speculative execution optimizaion. C# developers did the right thing by introducing "stackalloc" and believe me it is really required for next-gen optimizations – Abdurrahim Oct 07 '21 at 21:38
3

You can create your own HeapArray. It's not that complicated if you read alloc's docs:

use std::alloc::{alloc, dealloc, Layout};
pub struct HeapArray<T> {
    ptr: *mut T,
    len: usize,
}

impl<T> HeapArray<T> {
    pub fn new(len: usize) -> Self {
        let ptr = unsafe {
            let layout = Layout::from_size_align_unchecked(len, std::mem::size_of::<T>());
            alloc(layout) as *mut T
        };
        Self { ptr, len }
    }
    pub fn get(&self, idx: usize) -> Option<&T> {
        if idx < self.len {
            unsafe { Some(&*(self.ptr.add(idx))) }
        } else {
            None
        }
    }
    pub fn get_mut(&self, idx: usize) -> Option<&mut T> {
        if idx < self.len {
            unsafe { Some(&mut *(self.ptr.add(idx))) }
        } else {
            None
        }
    }
    pub fn len(&self) -> usize {
        self.len
    }
}


impl<T> Drop for HeapArray<T> {
    fn drop(&mut self) {
        unsafe {
            dealloc(
                self.ptr as *mut u8,
                Layout::from_size_align_unchecked(self.len, std::mem::size_of::<T>()),
            )
        };
    }
}

impl<T> std::ops::Index<usize> for HeapArray<T> {
    type Output = T;
    fn index(&self, index: usize) -> &Self::Output {
        self.get(index).unwrap()
    }
}
impl<T> std::ops::IndexMut<usize> for HeapArray<T> {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        self.get_mut(index).unwrap()
    }
}

You may also add methods like as_slice, get_unchecked, etc.

Tianyi Shi
  • 883
  • 10
  • 15
  • size argument of `from_size_align_unchecked` is in bytes. So you have to multiply `self.len` to `std::mem::size_of::()` to calculate total size. – Majid Azimi Dec 08 '20 at 16:07
  • 2
    How is this different from `Vec`? It does a heap allocation, just like `Vec` does. – Neil Mayhew Dec 16 '21 at 00:13
  • 15
    This code is 100% memory unsafe and should not be used by anyone. `let ha = HeapArray::::new(10); println!("{}", ha[7]);` causes a segfault. Working cases would result in memory leaks. "It's not that complicated" to write buggy code. – Shepmaster Dec 22 '21 at 19:27