cargo : arrayref @ 0.3.9
README.md
110 lines · markdown
# arrayref[](https://travis-ci.org/droundy/arrayref)[](https://coveralls.io/github/droundy/arrayref?branch=master)[Documentation](https://docs.rs/arrayref)This is a very small rust module, which contains just four macros, forthe taking of array references to slices of... sliceable things.These macros (which are awkwardly named) should be perfectly safe, andhave seen just a tad of code review.## Why would I want this?The goal of arrayref is to enable the effective use of APIs thatinvolve array references rather than slices, for situations whereparameters must have a given size. As an example, consider the`byteorder` crate. This is a very nice crate with a simple APIcontaining functions that look like:```rustfn read_u16(buf: &[u8]) -> u16;fn write_u16(buf: &mut [u8], n: u16);```Looking at this, you might wonder why they accept a slice reference asinput. After all, they always want just two bytes. These functionsmust panic if given a slice that is too small, which means that unlessthey are inlined, then a runtime bounds-check is forced, even if itmay be statically known that the input is the right size.Wouldn't it be nicer if we had functions more like```rustfn read_u16_array(buf: &[u8; 2]) -> u16;fn write_u16_array(buf: &mut [u8; 2], n: u16);```The type signature would tell users precisely what size of input isrequired, and the compiler could check at compile time that the inputis of the appropriate size: this sounds like the zero-costabstractions rust is famous for! However, there is a catch, whicharises when you try to *use* these nicer functions, which is thatusually you are looking at two bytes in a stream. So, e.g. considerthat we are working with a hypothetical (and simplified) ipv6 address.Doing this with our array version (which looks so beautiful in termsof accurately describing what we want!) looks terrible:```rustlet addr: &[u8; 16] = ...;let mut segments = [0u16; 8];// array-based APIfor i in 0 .. 8 { let mut two_bytes = [addr[2*i], addr[2*i+1]]; segments[i] = read_u16_array(&two_bytes);}// slice-based APIfor i in 0 .. 8 { segments[i] = read_u16(&addr[2*i..]);}```The array-based approach looks way worse. We need to create a freshcopy of the bytes, just so it will be in an array of the proper size!Thus the whole "zero-cost abstraction" argument for using arrayreferences fails. The trouble is that there is no (safe) way (until[RFC 495][1] lands) to obtain an array reference to a portion of alarger array or slice. Doing so is the equivalent of taking a slicewith a size known at compile time, and ought to be built into thelanguage.[1]: https://github.com/rust-lang/rfcs/blob/master/text/0495-array-pattern-changes.mdThe arrayref crate allows you to do this kind of slicing. So theabove (very contrived) example can be implemented with arrayreferences as:```rustlet addr: &[u8; 16] = ...;let mut segments = [0u16; 8];// array-based API with arrayreffor i in 0 .. 8 { segments[i] = read_u16_array(array_ref![addr,2*i,2]);}```Here the `array_ref![addr,2*i,2]` macro allows us to take an arrayreference to a slice consisting of two bytes starting at `2*i`. Apartfrom the syntax (less nice than slicing), it is essentially the sameas the slice approach. However, this code makes explicit theneed for precisely *two* bytes both in the caller, and in the functionsignature.This module provides three other macros providing relatedfunctionality, which you can read about in the[documentation](https://droundy.github.io/arrayref).For an example of how these macros can be used in an actual program,see [my rust translation of tweetnacl][2], which uses `arrayref`to almost exclusively accept array references in functions, with theonly exception being those which truly expect data of arbitrarylength. In my opinion, the result is code that is far more legiblethan the original C code, since the size of each argument isexplicit. Moreover (although I have not tested this), the use ofarray references rather than slices *should* result in far fewerbounds checks, since almost all sizes are known at compile time.[2]: https://github.com/droundy/onionsalt/blob/master/src/crypto/mod.rs