Rollup merge of #126100 - scottmcm:decaveat-map, r=Mark-Simulacrum

Reword the caveats on `array::map`

Thanks to #107634 and some improvements in LLVM (particularly [`dead_on_unwind`](https://llvm.org/docs/LangRef.html#parameter-attributes)), the method actually optimizes reasonably well now.

So focus the discussion on the fundamental ordering differences where the optimizer might never be able to fix it because of the different behaviour, and keep encouraging `Iterator::map` where an array wasn't actually ever needed.
This commit is contained in:
Jonathan Brouwer 2026-02-08 21:06:29 +01:00 committed by GitHub
commit c33bd8aa53
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -516,20 +516,47 @@ impl<T, const N: usize> [T; N] {
///
/// # Note on performance and stack usage
///
/// Unfortunately, usages of this method are currently not always optimized
/// as well as they could be. This mainly concerns large arrays, as mapping
/// over small arrays seem to be optimized just fine. Also note that in
/// debug mode (i.e. without any optimizations), this method can use a lot
/// of stack space (a few times the size of the array or more).
/// Note that this method is *eager*. It evaluates `f` all `N` times before
/// returning the new array.
///
/// Therefore, in performance-critical code, try to avoid using this method
/// on large arrays or check the emitted code. Also try to avoid chained
/// maps (e.g. `arr.map(...).map(...)`).
/// That means that `arr.map(f).map(g)` is, in general, *not* equivalent to
/// `array.map(|x| g(f(x)))`, as the former calls `f` 4 times then `g` 4 times,
/// whereas the latter interleaves the calls (`fgfgfgfg`).
///
/// In many cases, you can instead use [`Iterator::map`] by calling `.iter()`
/// or `.into_iter()` on your array. `[T; N]::map` is only necessary if you
/// really need a new array of the same size as the result. Rust's lazy
/// iterators tend to get optimized very well.
/// A consequence of this is that it can have fairly-high stack usage, especially
/// in debug mode or for long arrays. The backend may be able to optimize it
/// away, but especially for complicated mappings it might not be able to.
///
/// If you're doing a one-step `map` and really want an array as the result,
/// then absolutely use this method. Its implementation uses a bunch of tricks
/// to help the optimizer handle it well. Particularly for simple arrays,
/// like `[u8; 3]` or `[f32; 4]`, there's nothing to be concerned about.
///
/// However, if you don't actually need an *array* of the results specifically,
/// just to process them, then you likely want [`Iterator::map`] instead.
///
/// For example, rather than doing an array-to-array map of all the elements
/// in the array up-front and only iterating after that completes,
///
/// ```
/// # let my_array = [1, 2, 3];
/// # let f = |x: i32| x + 1;
/// for x in my_array.map(f) {
/// // ...
/// }
/// ```
///
/// It's often better to use an iterator along the lines of
///
/// ```
/// # let my_array = [1, 2, 3];
/// # let f = |x: i32| x + 1;
/// for x in my_array.into_iter().map(f) {
/// // ...
/// }
/// ```
///
/// as that's more likely to avoid large temporaries.
///
///
/// # Examples