5 releases
0.1.4 | May 28, 2021 |
---|---|
0.1.3 | May 28, 2021 |
0.1.2 | May 27, 2021 |
0.1.1 | May 27, 2021 |
0.1.0 | May 27, 2021 |
#47 in #impl
Used in array_iter_tools
12KB
146 lines
array_builder
ArrayBuilder impl for Rust. https://github.com/rust-lang/rfcs/pull/3131
lib.rs
:
Dynamic array initialisation is very dangerous currently. The safest way is to initialize one with a default value
let mut array = [0; 32];
for i in 0..32 {
array[i] = i;
}
This is not possible in general though. For any type [T; N]
,
T either needs to be Copy
, or there needs to be a const t: T
.
This is definitely not always the case.
The second problem is efficiency. In the example above, we are filling an array with zeros, just to replace them. While the compiler can sometimes optimise this away, it's nice to have the guarantee.
So, what's the alternative? How about MaybeUninit
! Although, it's not that simple.
Take the following example, which uses completely safe Rust! Can you spot the error?
let mut uninit: [MaybeUninit<String>; 8] = MaybeUninit::uninit_array();
uninit[0].write("foo".to_string());
uninit[1].write("bar".to_string());
uninit[2].write("baz".to_string());
panic!("oops");
Did you spot it? Right there is a memory leak. The key here is that
MaybeUninit
does not implement Drop
. This makes sense
since the value could be uninitialized, and calling Drop
on an
uninitialized value is undefined behaviour. The result of this is that
the 3 String
values we did initialize never got dropped!
Now, this is safe according to Rust. Leaking memory is not undefined
behaviour. But it's still not something we should promote.
What other options do we have? The only solution is to provide a new
struct
that wraps the array, and properly implements Drop
. That
way, if drop
is called, we can make sure any initialized values get
dropped properly. This is exactly what ArrayBuilder
provides.
use array_builder::ArrayBuilder;
let mut uninit: ArrayBuilder<String, 8> = ArrayBuilder::new();
uninit.push("foo".to_string());
uninit.push("bar".to_string());
uninit.push("baz".to_string());
panic!("oops"); // ArrayBuilder drops the 3 values above for you
use array_builder::ArrayBuilder;
let mut uninit: ArrayBuilder<String, 3> = ArrayBuilder::new();
uninit.push("foo".to_string());
uninit.push("bar".to_string());
uninit.push("baz".to_string());
let array: [String; 3] = uninit.build().unwrap();
You can also take a peek at what the current set of initialised values are
use array_builder::ArrayBuilder;
let mut uninit: ArrayBuilder<usize, 4> = ArrayBuilder::new();
uninit.push(1);
uninit.push(2);
uninit.push(3);
// we can't build just yet
let mut uninit = uninit.build().unwrap_err();
let slice: &[usize] = &uninit;
assert_eq!(&[1, 2, 3], slice);
uninit.push(4);
assert_eq!([1, 2, 3, 4], uninit.build().unwrap());