3

The general setup is I have an array of values I'd like to map() and then chain() with 1 additional value. I've learned from this answer that the proper way to construct that final value is to use std::iter::once. This works and eliminated the below problem, but I would still like to understand it better.

In my broken, likely rust-anti-pattern-riddled example, I was using an array of a single element and then calling into_iter(). This produced a value / reference type-mismatch in the chain.

Question: What is the Rust-idiomatic mechanism for correcting this value / reference mismatch? Particularly if clone and copy are unavailable.

Background: Why is there a type mis-match to begin with?

This much I believe I understand. Based on the definition of std::iter::Map, the item type for the iterator is type Item = B where B is constrained by F: FnMut(<I as Iterator>::Item) -> B (i.e. the mapped type). However array defines the following 2 IntoIterator implementations, both of which appear to produce references.

impl<'a, const N: usize, T> IntoIterator for &'a [T; N] where [T; N]: LengthAtMost32, type Item = &'a T impl<'a, const N: usize, T> IntoIterator for &'a mut [T; N] where [T; N]: LengthAtMost32, type Item = &'a mut T 

Example demonstrating the issue:

#[derive(PartialEq, Eq, Clone, Copy)] enum Enum1 { A, B, C } #[derive(PartialEq, Eq, Clone, Copy)] enum Enum2 { X, Y, Z } struct Data { // Other data omitted e1: Enum1, e2: Enum2 } struct Consumer { // Other data omitted /** Predicate which evaluates if this consumer can consume given Data */ consumes: Box<dyn Fn(&Data) -> bool> } fn main() { // Objective: 3 consumers which consume data with A, B, and X respectively let v: Vec<Consumer> = [Enum1::A, Enum1::B].iter() .map(|&e1| Consumer { consumes: Box::new(move |data| data.e1 == e1) }) // This chain results in an iterator type-mismatch: // expected &Consumer, found Consumer .chain([Consumer { consumes: Box::new(move |data| data.e2 == Enum2::X) }].into_iter()) .collect(); // Fails as well due to the chain failure } 

Error:

error[E0271]: type mismatch resolving `<std::slice::Iter<'_, Consumer> as std::iter::IntoIterator>::Item == Consumer` --> src/main.rs:52:10 | 52 | .chain([Consumer { consumes: Box::new(move |data| data.e2 == Enum2::X) }].into_iter()) | ^^^^^ expected reference, found struct `Consumer` | = note: expected type `&Consumer` found type `Consumer` 

Rust playground example.

1 Answer 1

2

There is a long-standing issue regarding this. The technical details are a bit heavy, but essentially, due to underlying, technical reasons, you cannot take ownership of a fixed-size array and return owned references without a lot of hocus pocus. This becomes obvious when you think about what a fixed-size array is and how it is stored in memory, and how you can get elements out without cloning them.

As a result, due to the implementations you found already, you can only get borrowed references. You can bypass this with arrayvec (as they have a sound implementation of IntoIterator for ArrayVec with owned types), or you can require that all your T: Clone and deal with it that way, at a cost of extra items in memory (temporarily; 90% of the time the compiler optimizes this away).

Sign up to request clarification or add additional context in comments.

1 Comment

Rust edition 2021 will support IntoIterator for arrays. blog.rust-lang.org/2021/05/11/…

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.