Array, slices and references - rust notes

✍️ Written on 2020-11-30 in 845 words.
Part of software-development programming-languages rustlang

Motivation

When implementing cryptographic software in rust, you often deal with arrays. Eventually, you stumble upon rust’s strict type system. This serves as a tiny, trivial array types cheatsheet.

Starting with an example

Consider the following source code:

fn main() {
    let a = [1, 2, 3];
}
  • a is of type [{integer}; 3] (thus an array with 3 elements)

  • a[..] is of type [{integer}] (thus a slice which is a view into contiguous memory consisting of a pointer and a length internally)

  • &a or &a[..] simply takes a reference to the respective types

Debugging the type

How can you debug the type?

  • Either trigger a compiler error. Use an assignment like let x: bool = a[..]; or call an non-existing method a.non_existent_method().

  • By the way, &a[..].non_existent_method() calls &(a[..].non_existent_method()). Hence (&a[..]).non_existent_method() is required to call the method on its respective slice reference.

Another (limited) way is to use type_name which does not require nightly:

use std::any::type_name;

fn type_as_string<T>(_: &T) -> String {
    String::from(std::any::type_name::<T>())
}

fn main() {
    let a = [1, 2, 3];
    println!("{}", type_as_string(&a));
    // print "[i32; 3]"
}

The limitation comes from not being able to call type_as_string(&a[..]). The compiler will complain that error[E0277]: the size for values of type [{integer}] cannot be known at compilation time. This is the major limitation of slice which makes them unusable for function parameters and return values. With respect to debugging slices, you are stuck with the two approaches above. Be aware that type_as_string(&&a[..]) can still be called since these are simply references.

Compatibility

fn func(arg: {dst}) {
    println!("{:?}", arg);
}

fn main() {
    let a: [u8; 3] = [1, 2, 3];
    func({src});
}

So, which values can be inserted for {src} and {dst}?

src ↓ \ dst → [u8; 3] [u8] &[u8; 3] &[u8]

a

yes

no, slice cannot be a parameter type

no, not a reference

no, not a reference

a[..]

no, slice is not an array

no, slice cannot be a parameter type

no, not a reference

no, not a reference

&a

no, not an array, but reference

no, slice cannot be a parameter type

yes

yes, length is dropped

&a[..]

no, not an array, but reference

no, slice cannot be a parameter type

no, slice not array; lacking length

yes

Conclusion

Arrays are containers with a length. If you loose its length, you have a slice, which is almost unusable. But for both types, you can always take references which work intuitively.