]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | # arrayref |
2 | ||
3 | [![Build Status](https://travis-ci.org/droundy/arrayref.svg?branch=master)](https://travis-ci.org/droundy/arrayref) | |
4 | [![Coverage Status](https://coveralls.io/repos/droundy/arrayref/badge.svg?branch=master&service=github)](https://coveralls.io/github/droundy/arrayref?branch=master) | |
5 | ||
6 | [Documentation](https://docs.rs/arrayref) | |
7 | ||
8 | This is a very small rust module, which contains just four macros, for | |
9 | the taking of array references to slices of... sliceable things. | |
10 | These macros (which are awkwardly named) should be perfectly safe, and | |
11 | have seen just a tad of code review. | |
12 | ||
13 | ## Why would I want this? | |
14 | ||
15 | The goal of arrayref is to enable the effective use of APIs that | |
16 | involve array references rather than slices, for situations where | |
17 | parameters must have a given size. As an example, consider the | |
18 | `byteorder` crate. This is a very nice crate with a simple API | |
19 | containing functions that look like: | |
20 | ||
21 | ```rust | |
22 | fn read_u16(buf: &[u8]) -> u16; | |
23 | fn write_u16(buf: &mut [u8], n: u16); | |
24 | ``` | |
25 | ||
26 | Looking at this, you might wonder why they accept a slice reference as | |
27 | input. After all, they always want just two bytes. These functions | |
28 | must panic if given a slice that is too small, which means that unless | |
29 | they are inlined, then a runtime bounds-check is forced, even if it | |
30 | may be statically known that the input is the right size. | |
31 | ||
32 | Wouldn't it be nicer if we had functions more like | |
33 | ||
34 | ```rust | |
35 | fn read_u16_array(buf: &[u8; 2]) -> u16; | |
36 | fn write_u16_array(buf: &mut [u8; 2], n: u16); | |
37 | ``` | |
38 | ||
39 | The type signature would tell users precisely what size of input is | |
40 | required, and the compiler could check at compile time that the input | |
41 | is of the appropriate size: this sounds like the zero-cost | |
42 | abstractions rust is famous for! However, there is a catch, which | |
43 | arises when you try to *use* these nicer functions, which is that | |
44 | usually you are looking at two bytes in a stream. So, e.g. consider | |
45 | that we are working with a hypothetical (and simplified) ipv6 address. | |
46 | ||
47 | Doing this with our array version (which looks so beautiful in terms | |
48 | of accurately describing what we want!) looks terrible: | |
49 | ||
50 | ```rust | |
51 | let addr: &[u8; 16] = ...; | |
52 | let mut segments = [0u16; 8]; | |
53 | // array-based API | |
54 | for i in 0 .. 8 { | |
55 | let mut two_bytes = [addr[2*i], addr[2*i+1]]; | |
56 | segments[i] = read_u16_array(&two_bytes); | |
57 | } | |
58 | // slice-based API | |
59 | for i in 0 .. 8 { | |
60 | segments[i] = read_u16(&addr[2*i..]); | |
61 | } | |
62 | ``` | |
63 | ||
64 | The array-based approach looks way worse. We need to create a fresh | |
65 | copy of the bytes, just so it will be in an array of the proper size! | |
66 | Thus the whole "zero-cost abstraction" argument for using array | |
67 | references fails. The trouble is that there is no (safe) way (until | |
68 | [RFC 495][1] lands) to obtain an array reference to a portion of a | |
69 | larger array or slice. Doing so is the equivalent of taking a slice | |
70 | with a size known at compile time, and ought to be built into the | |
71 | language. | |
72 | ||
73 | [1]: https://github.com/rust-lang/rfcs/blob/master/text/0495-array-pattern-changes.md | |
74 | ||
75 | The arrayref crate allows you to do this kind of slicing. So the | |
76 | above (very contrived) example can be implemented with array | |
77 | references as: | |
78 | ||
79 | ```rust | |
80 | let addr: &[u8; 16] = ...; | |
81 | let mut segments = [0u16; 8]; | |
82 | // array-based API with arrayref | |
83 | for i in 0 .. 8 { | |
84 | segments[i] = read_u16_array(array_ref![addr,2*i,2]); | |
85 | } | |
86 | ``` | |
87 | ||
88 | Here the `array_ref![addr,2*i,2]` macro allows us to take an array | |
89 | reference to a slice consisting of two bytes starting at `2*i`. Apart | |
90 | from the syntax (less nice than slicing), it is essentially the same | |
91 | as the slice approach. However, this code makes explicit the | |
92 | need for precisely *two* bytes both in the caller, and in the function | |
93 | signature. | |
94 | ||
95 | This module provides three other macros providing related | |
96 | functionality, which you can read about in the | |
97 | [documentation](https://droundy.github.io/arrayref). | |
98 | ||
99 | For an example of how these macros can be used in an actual program, | |
100 | see [my rust translation of tweetnacl][2], which uses `arrayref` | |
101 | to almost exclusively accept array references in functions, with the | |
102 | only exception being those which truly expect data of arbitrary | |
103 | length. In my opinion, the result is code that is far more legible | |
104 | than the original C code, since the size of each argument is | |
105 | explicit. Moreover (although I have not tested this), the use of | |
106 | array references rather than slices *should* result in far fewer | |
107 | bounds checks, since almost all sizes are known at compile time. | |
108 | ||
109 | [2]: https://github.com/droundy/onionsalt/blob/master/src/crypto.rs |