Add IntoIterator for Box<[T]>

Summary

  • Boxed slices implement IntoIterator in all editions.
  • Calls to IntoIterator::into_iter are hidden in editions prior to 2024 when using method call syntax (i.e., boxed_slice.into_iter()). So, boxed_slice.into_iter() still resolves to (&(*boxed_slice)).into_iter() as it has before.
  • boxed_slice.into_iter() changes meaning to call IntoIterator::into_iter in Rust 2024.

Details

Until Rust 1.80, IntoIterator was not implemented for boxed slices. In prior versions, if you called .into_iter() on a boxed slice, the method call would automatically dereference from Box<[T]> to &[T], and return an iterator that yielded references of &T. For example, the following worked in prior versions:

#![allow(unused)]
fn main() {
// Example of behavior in previous editions.
let my_boxed_slice: Box<[u32]> = vec![1, 2, 3].into_boxed_slice();
// Note: .into_iter() was required in versions older than 1.80
for x in my_boxed_slice.into_iter() {
    // x is of type &u32 in editions prior to 2024
}
}

In Rust 1.80, implementations of IntoIterator were added for boxed slices. This allows iterating over elements of the slice by-value instead of by-reference:

#![allow(unused)]
fn main() {
// NEW as of 1.80, all editions
let my_boxed_slice: Box<[u32]> = vec![1, 2, 3].into_boxed_slice();
for x in my_boxed_slice { // notice no need for calling .into_iter()
    // x is of type u32
}
}

This example is allowed on all editions because previously this was an error since for loops do not automatically dereference like the .into_iter() method call does.

However, this would normally be a breaking change because existing code that manually called .into_iter() on a boxed slice would change from having an iterator over references to an iterator over values. To resolve this problem, method calls of .into_iter() on boxed slices have edition-dependent behavior. In editions before 2024, it continues to return an iterator over references, and starting in Edition 2024 it returns an iterator over values.

#![allow(unused)]
fn main() {
// Example of changed behavior in Edition 2024
let my_boxed_slice: Box<[u32]> = vec![1, 2, 3].into_boxed_slice();
// Example of old code that still manually calls .into_iter()
for x in my_boxed_slice.into_iter() {
    // x is now type u32 in Edition 2024
}
}

Migration

The boxed_slice_into_iter lint will automatically modify any calls to .into_iter() on boxed slices to call .iter() instead to retain the old behavior of yielding references. This lint is part of the rust-2024-compatibility lint group, which will automatically be applied when running cargo fix --edition. To migrate your code to be Rust 2024 Edition compatible, run:

cargo fix --edition

For example, this will change:

fn main() {
    let my_boxed_slice: Box<[u32]> = vec![1, 2, 3].into_boxed_slice();
    for x in my_boxed_slice.into_iter() {
        // x is of type &u32
    }
}

to be:

fn main() {
    let my_boxed_slice: Box<[u32]> = vec![1, 2, 3].into_boxed_slice();
    for x in my_boxed_slice.iter() {
        // x is of type &u32
    }
}

The boxed_slice_into_iter lint is defaulted to warn on all editions, so unless you have manually silenced the lint, you should already see it before you migrate.