Как правильно заставить итератор возвращать значение вместо ссылки (или наоборот)? - PullRequest
2 голосов
/ 13 октября 2019

Общая настройка: у меня есть массив значений, которые я бы хотел map(), а затем chain() с 1 дополнительным значением. Из этого ответа я узнал, что правильный способ построить это окончательное значение - использовать std::iter::once. Это работает и устранило нижеприведенную проблему, но я все же хотел бы понять ее лучше .

В моем сломанном, вероятно, ржавом-анти-шаблонно-пронизанном примере я использовал массиводин элемент, а затем вызывать into_iter(). Это привело к несовпадению значений / ссылок в цепочке.

Вопрос: Что такое механизм Rust-idiomatic для исправления этого несоответствия значений / ссылок? Особенно, если clone и copy недоступны.

Справочная информация: Почему начинается несоответствие типов для начала?

Я так понимаю, что понимаю. Основываясь на определении std :: iter :: Map , тип элемента для итератора равен type Item = B, где B ограничен F: FnMut(<I as Iterator>::Item) -> B (то есть сопоставленный тип). Однако array определяет следующие 2 IntoIterator реализации, каждая из которых, по-видимому, создает ссылки.

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

Пример, демонстрирующий проблему:

#[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[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 .

1 Ответ

1 голос
/ 13 октября 2019

Существует давняя проблема по этому поводу. Технические детали немного тяжелы, но по сути, по техническим причинам, вы не можете стать владельцем массива фиксированного размера и возвращать собственные ссылки без особого внимания. Это становится очевидным, когда вы думаете о том, что такое массив фиксированного размера и как он хранится в памяти, и как вы можете извлекать элементы без их клонирования.

В результате из-за уже найденных реализацийВы можете получить только заимствованные ссылки. Вы можете обойти это с помощью arrayvec (поскольку они имеют звуковую реализацию IntoIterator для ArrayVec с собственными типами), или вы можете потребовать, чтобы все ваши T: Clone и обрабатывали это таким образом, за счет дополнительных элементов в памяти (временно; 90% времени компилятор оптимизирует это).

...