]>
Commit | Line | Data |
---|---|---|
85aaf69f SL |
1 | % Input to functions and methods |
2 | ||
3 | ### Let the client decide when to copy and where to place data. [FIXME: needs RFC] | |
4 | ||
5 | #### Copying: | |
6 | ||
7 | Prefer | |
8 | ||
9 | ```rust | |
10 | fn foo(b: Bar) { | |
11 | // use b as owned, directly | |
12 | } | |
13 | ``` | |
14 | ||
15 | over | |
16 | ||
17 | ```rust | |
18 | fn foo(b: &Bar) { | |
19 | let b = b.clone(); | |
20 | // use b as owned after cloning | |
21 | } | |
22 | ``` | |
23 | ||
24 | If a function requires ownership of a value of unknown type `T`, but does not | |
25 | otherwise need to make copies, the function should take ownership of the | |
26 | argument (pass by value `T`) rather than using `.clone()`. That way, the caller | |
27 | can decide whether to relinquish ownership or to `clone`. | |
28 | ||
29 | Similarly, the `Copy` trait bound should only be demanded it when absolutely | |
30 | needed, not as a way of signaling that copies should be cheap to make. | |
31 | ||
32 | #### Placement: | |
33 | ||
34 | Prefer | |
35 | ||
36 | ```rust | |
37 | fn foo(b: Bar) -> Bar { ... } | |
38 | ``` | |
39 | ||
40 | over | |
41 | ||
42 | ```rust | |
43 | fn foo(b: Box<Bar>) -> Box<Bar> { ... } | |
44 | ``` | |
45 | ||
46 | for concrete types `Bar` (as opposed to trait objects). This way, the caller can | |
47 | decide whether to place data on the stack or heap. No overhead is imposed by | |
48 | letting the caller determine the placement. | |
49 | ||
50 | ### Minimize assumptions about parameters. [FIXME: needs RFC] | |
51 | ||
52 | The fewer assumptions a function makes about its inputs, the more widely usable | |
53 | it becomes. | |
54 | ||
55 | #### Minimizing assumptions through generics: | |
56 | ||
57 | Prefer | |
58 | ||
59 | ```rust | |
62682a34 | 60 | fn foo<T: Iterator<i32>>(c: T) { ... } |
85aaf69f SL |
61 | ``` |
62 | ||
63 | over any of | |
64 | ||
65 | ```rust | |
62682a34 SL |
66 | fn foo(c: &[i32]) { ... } |
67 | fn foo(c: &Vec<i32>) { ... } | |
68 | fn foo(c: &SomeOtherCollection<i32>) { ... } | |
85aaf69f SL |
69 | ``` |
70 | ||
71 | if the function only needs to iterate over the data. | |
72 | ||
73 | More generally, consider using generics to pinpoint the assumptions a function | |
74 | needs to make about its arguments. | |
75 | ||
76 | On the other hand, generics can make it more difficult to read and understand a | |
77 | function's signature. Aim for "natural" parameter types that a neither overly | |
78 | concrete nor overly abstract. See the discussion on | |
54a0048b | 79 | [traits](../traits/README.md) for more guidance. |
85aaf69f SL |
80 | |
81 | ||
82 | #### Minimizing ownership assumptions: | |
83 | ||
84 | Prefer either of | |
85 | ||
86 | ```rust | |
87 | fn foo(b: &Bar) { ... } | |
88 | fn foo(b: &mut Bar) { ... } | |
89 | ``` | |
90 | ||
91 | over | |
92 | ||
93 | ```rust | |
94 | fn foo(b: Bar) { ... } | |
95 | ``` | |
96 | ||
97 | That is, prefer borrowing arguments rather than transferring ownership, unless | |
98 | ownership is actually needed. | |
99 | ||
100 | ### Prefer compound return types to out-parameters. [FIXME: needs RFC] | |
101 | ||
102 | Prefer | |
103 | ||
104 | ```rust | |
105 | fn foo() -> (Bar, Bar) | |
106 | ``` | |
107 | ||
108 | over | |
109 | ||
110 | ```rust | |
111 | fn foo(output: &mut Bar) -> Bar | |
112 | ``` | |
113 | ||
114 | for returning multiple `Bar` values. | |
115 | ||
116 | Compound return types like tuples and structs are efficiently compiled | |
117 | and do not require heap allocation. If a function needs to return | |
118 | multiple values, it should do so via one of these types. | |
119 | ||
120 | The primary exception: sometimes a function is meant to modify data | |
121 | that the caller already owns, for example to re-use a buffer: | |
122 | ||
123 | ```rust | |
62682a34 | 124 | fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> |
85aaf69f SL |
125 | ``` |
126 | ||
e9174d1e | 127 | (From the [Read trait](https://doc.rust-lang.org/stable/std/io/trait.Read.html#tymethod.read).) |
85aaf69f SL |
128 | |
129 | ### Consider validating arguments, statically or dynamically. [FIXME: needs RFC] | |
130 | ||
131 | _Note: this material is closely related to | |
132 | [library-level guarantees](../../safety/lib-guarantees.md)._ | |
133 | ||
134 | Rust APIs do _not_ generally follow the | |
e9174d1e | 135 | [robustness principle](https://en.wikipedia.org/wiki/Robustness_principle): "be |
85aaf69f SL |
136 | conservative in what you send; be liberal in what you accept". |
137 | ||
138 | Instead, Rust code should _enforce_ the validity of input whenever practical. | |
139 | ||
140 | Enforcement can be achieved through the following mechanisms (listed | |
141 | in order of preference). | |
142 | ||
143 | #### Static enforcement: | |
144 | ||
145 | Choose an argument type that rules out bad inputs. | |
146 | ||
147 | For example, prefer | |
148 | ||
149 | ```rust | |
e9174d1e SL |
150 | enum FooMode { |
151 | Mode1, | |
152 | Mode2, | |
153 | Mode3, | |
154 | } | |
155 | fn foo(mode: FooMode) { ... } | |
85aaf69f SL |
156 | ``` |
157 | ||
158 | over | |
159 | ||
160 | ```rust | |
e9174d1e SL |
161 | fn foo(mode2: bool, mode3: bool) { |
162 | assert!(!mode2 || !mode3); | |
163 | ... | |
164 | } | |
85aaf69f SL |
165 | ``` |
166 | ||
85aaf69f | 167 | Static enforcement usually comes at little run-time cost: it pushes the |
e9174d1e SL |
168 | costs to the boundaries. It also catches bugs early, during compilation, |
169 | rather than through run-time failures. | |
85aaf69f SL |
170 | |
171 | On the other hand, some properties are difficult or impossible to | |
172 | express using types. | |
173 | ||
174 | #### Dynamic enforcement: | |
175 | ||
176 | Validate the input as it is processed (or ahead of time, if necessary). Dynamic | |
177 | checking is often easier to implement than static checking, but has several | |
178 | downsides: | |
179 | ||
180 | 1. Runtime overhead (unless checking can be done as part of processing the input). | |
181 | 2. Delayed detection of bugs. | |
e9174d1e | 182 | 3. Introduces failure cases, either via `panic!` or `Result`/`Option` types (see |
85aaf69f SL |
183 | the [error handling guidelines](../../errors/README.md)), which must then be |
184 | dealt with by client code. | |
185 | ||
186 | #### Dynamic enforcement with `debug_assert!`: | |
187 | ||
188 | Same as dynamic enforcement, but with the possibility of easily turning off | |
189 | expensive checks for production builds. | |
190 | ||
191 | #### Dynamic enforcement with opt-out: | |
192 | ||
193 | Same as dynamic enforcement, but adds sibling functions that opt out of the | |
194 | checking. | |
195 | ||
196 | The convention is to mark these opt-out functions with a suffix like | |
197 | `_unchecked` or by placing them in a `raw` submodule. | |
198 | ||
199 | The unchecked functions can be used judiciously in cases where (1) performance | |
200 | dictates avoiding checks and (2) the client is otherwise confident that the | |
201 | inputs are valid. | |
202 | ||
203 | > **[FIXME]** Should opt-out functions be marked `unsafe`? |