]>
Commit | Line | Data |
---|---|---|
17df50a5 XL |
1 | # IntoIterator for arrays |
2 | ||
3 | ## Summary | |
4 | ||
136023e0 XL |
5 | - Arrays implement `IntoIterator` in *all* editions. |
6 | - Calls to `IntoIterator::into_iter` are *hidden* in Rust 2015 and Rust 2018 when using method call syntax | |
7 | (i.e., `array.into_iter()`). So, `array.into_iter()` still resolves to `(&array).into_iter()` as it | |
8 | has before. | |
9 | - `array.into_iter()` changes meaning to be the call to `IntoIterator::into_inter` in Rust 2021. | |
10 | ||
17df50a5 XL |
11 | ## Details |
12 | ||
13 | Until Rust 1.53, only *references* to arrays implement `IntoIterator`. | |
14 | This means you can iterate over `&[1, 2, 3]` and `&mut [1, 2, 3]`, | |
15 | but not over `[1, 2, 3]` directly. | |
16 | ||
17 | ```rust,ignore | |
18 | for &e in &[1, 2, 3] {} // Ok :) | |
19 | ||
20 | for e in [1, 2, 3] {} // Error :( | |
21 | ``` | |
22 | ||
23 | This has been [a long-standing issue][25], but the solution is not as simple as it seems. | |
24 | Just [adding the trait implementation][20] would break existing code. | |
25 | `array.into_iter()` already compiles today because that implicitly calls | |
26 | `(&array).into_iter()` due to [how method call syntax works][22]. | |
27 | Adding the trait implementation would change the meaning. | |
28 | ||
136023e0 | 29 | Usually this type of breakage (adding a trait implementation) is categorized as 'minor' and acceptable. |
17df50a5 XL |
30 | But in this case there is too much code that would be broken by it. |
31 | ||
32 | It has been suggested many times to "only implement `IntoIterator` for arrays in Rust 2021". | |
33 | However, this is simply not possible. | |
34 | You can't have a trait implementation exist in one edition and not in another, | |
35 | since editions can be mixed. | |
36 | ||
136023e0 XL |
37 | Instead, the trait implementation was added in *all* editions (starting in Rust 1.53.0) |
38 | but with a small hack to avoid breakage until Rust 2021. | |
17df50a5 XL |
39 | In Rust 2015 and 2018 code, the compiler will still resolve `array.into_iter()` |
40 | to `(&array).into_iter()` like before, as if the trait implementation does not exist. | |
41 | This *only* applies to the `.into_iter()` method call syntax. | |
42 | It does not affect any other syntax such as `for e in [1, 2, 3]`, `iter.zip([1, 2, 3])` or | |
43 | `IntoIterator::into_iter([1, 2, 3])`. | |
44 | Those will start to work in *all* editions. | |
45 | ||
46 | While it's a shame that this required a small hack to avoid breakage, | |
136023e0 | 47 | this solution keeps the difference between the editions to an absolute minimum. |
17df50a5 XL |
48 | |
49 | [25]: https://github.com/rust-lang/rust/issues/25725 | |
50 | [20]: https://github.com/rust-lang/rust/pull/65819 | |
136023e0 XL |
51 | [22]: https://doc.rust-lang.org/book/ch05-03-method-syntax.html#wheres-the---operator |
52 | ||
53 | ## Migration | |
54 | ||
55 | A lint, `array_into_iter`, gets triggered whenever there is some call to `into_iter()` that will change | |
56 | meaning in Rust 2021. The `array_into_iter` lint has already been a warning by default on all editions | |
57 | since the 1.41 release (with several enhancements made in 1.55). If your code is already warning free, | |
58 | then it should already be ready to go for Rust 2021! | |
59 | ||
60 | You can automatically migrate your code to be Rust 2021 Edition compatible or ensure it is already compatible by | |
61 | running: | |
62 | ||
63 | ```sh | |
64 | cargo fix --edition | |
65 | ``` | |
66 | ||
67 | Because the difference between editions is small, the migration to Rust 2021 is fairly straight-forward. | |
68 | ||
69 | For method calls of `into_iter` on arrays, the elements being implemented will change from references to owned values. | |
70 | ||
71 | For example: | |
72 | ||
73 | ```rust | |
74 | fn main() { | |
75 | let array = [1u8, 2, 3]; | |
76 | for x in array.into_iter() { | |
77 | // x is a `&u8` in Rust 2015 and Rust 2018 | |
78 | // x is a `u8` in Rust 2021 | |
79 | } | |
80 | } | |
81 | ``` | |
82 | ||
83 | The most straightforward way to migrate in Rust 2021, is by keeping the exact behavior from previous editions | |
84 | by calling `iter()` which also iterates over owned arrays by reference: | |
85 | ||
86 | ```rust | |
87 | fn main() { | |
88 | let array = [1u8, 2, 3]; | |
89 | for x in array.iter() { // <- This line changed | |
90 | // x is a `&u8` in all editions | |
91 | } | |
92 | } | |
93 | ``` | |
94 | ||
95 | ### Optional migration | |
96 | ||
97 | If you are using fully qualified method syntax (i.e., `IntoIterator::into_iter(array)`) in a previous edition, | |
98 | this can be upgraded to method call syntax (i.e., `array.into_iter()`). |