Почему я получаю FromIterator<&T>` не реализован для Vec<T>?

Я реализую тип коллекции, который содержит вектор структур. Я хочу реализовать кучу методов для сортировки моего вектора различными способами. Важно, чтобы каждая функция возвращала набор значений, потому что сайт вызова будет изменять результаты дальше, что может означать удаление или изменение значений, и ни одно из этих изменений не должно распространяться обратно на исходную коллекцию.

Структура очень проста:

#[derive(PartialEq, Debug, Clone)]
pub struct Shoe {
    size: u32,
    style: String,
}

Тип коллекции просто оборачивает структуру в вектор, например:

#[derive(Debug, PartialEq, Clone)]
pub struct ShoesInventory {
    shoes: Vec<Shoe>
}

Я хочу отфильтровать всю существующую обувь по заданному размеру и вернуть результат в виде отдельного вектора. По сути, повторяйте, фильтруйте и собирайте. Однако, когда я пишу это,

impl ShoesInventory {
    pub fn new(shoes: Vec<Shoe>) -> ShoesInventory {
        ShoesInventory { shoes }
    }

    pub fn shoes_in_size(&self, shoe_size: u32) -> Vec<Shoe> {
        self.shoes.iter().filter(| s| s.size == shoe_size).collect()
    }
}

Я получаю следующую ошибку компилятора

error[E0277]: a value of type `Vec<Shoe>` cannot be built from an iterator over elements of type `&Shoe`
    --> src/shoes.rs:18:9
     |
18   |         self.shoes.iter().filter(| s| s.size == shoe_size).collect()
     |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------- required by a bound introduced by this call
     |         |
     |         value of type `Vec<Shoe>` cannot be built from `std::iter::Iterator<Item=&Shoe>`
     |
     = help: the trait `FromIterator<&Shoe>` is not implemented for `Vec<Shoe>`

Если я попытаюсь клонировать элемент в замыкании, это ничего не исправит, и я все еще получаю ту же ошибку. Не совсем понятно, в чем проблема, потому что на другом векторе этот шаблон кода действительно работает. Например, когда вы используете другой вектор с примитивным типом, скажем, целым числом, итератор, карта/фильтр, шаблон сбора работают нормально.

let v1: Vec<i32> = vec![1, 2, 3];
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect(); // no problem here 

Однако, когда векторный элемент содержит структуру или строку, все становится не так.

Я так понимаю ошибка в основном говорит о том, что FromIterator не реализован, но почему? И как мне это исправить?

Код детской площадки

Подсказка: iter() дает вам ссылки, вам нужны собственные значения.

Chayim Friedman 04.12.2022 07:46

@ChayimFriedman Спасибо, клонирование итератора в основном исправило это: self.shoes.iter().cloned().filter(| s| s.size == shoes_size).collect()

Marvin.Hansen 04.12.2022 07:51

Если вы нашли решение самостоятельно, а не редактировали его в вопросе, вы должны вместо этого опубликовать ответ SO.

cafce25 04.12.2022 10:38

Пожалуйста, не редактируйте свой вопрос, чтобы быть ответом. Разместите ответ под своим вопросом, если хотите.

justANewb stands with Ukraine 05.12.2022 09:35

Хорошо, я понимаю. Я отметил ответ ниже как официальный ответ, потому что он указывает на очень важный аспект клонирования после фильтра, который напрямую относится к вопросу. Спасибо.

Marvin.Hansen 05.12.2022 09:39
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
6
66
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы сказали Rust, что хотите вернуть Vec<Shoe>, т.е. коллекция принадлежащих обувных предметов. Однако вы скармливаете ему последовательность ссылок на обувь.

В зависимости от варианта использования этой функции, вы можете пойти по крайней мере в нескольких направлениях.

Пока вызывающим эту функцию на самом деле не нужна собственная обувь, вы можете просто изменить функцию на это:

pub fn shoes_in_size(&self, shoe_size: u32) -> Vec<&Shoe> {
...

Затем вызывающий объект получит vec, содержащий ссылки на обувь, соответствующую условию фильтрации. Часто это именно то, что вы хотите — зачем делать дорогие клоны структуры Shoe, если вам это не нужно?

Обратите внимание, что компилятор rust автоматически гарантирует, что время жизни возвращаемых ссылок не превышает время жизни ShoeInventory, на которое они ссылаются.

Как правило, вы хотите вернуть принадлежащие вам предметы, если выполняется одно из следующих условий:

  • Вызывающий функцию захочет изменить элементы, возвращаемые функцией.
  • Время жизни возвращенных предметов может пережить коллекцию, которая их раздает.

В любом из этих случаев может быть более целесообразно клонировать их, как вы указали в комментариях. В этом случае, однако, я бы клонировал их после фильтрации, чтобы вы не клонировали элементы, а затем сразу же отбрасывали их:

pub fn shoes_in_size(&self, shoe_size: u32) -> Vec<Shoe> {
    self.shoes.iter().filter(| s| s.size == shoe_size).cloned().collect()
}

Отличное замечание @harmic. Это правда, что сайт вызова изменит результат, поэтому необходимо вернуть вектор значений. И да, клонирование после фильтра — лучший способ по любым стандартам. Я пометил ваши ответы как официальные и соответствующим образом обновил свой вопрос.

Marvin.Hansen 05.12.2022 09:36

Другие вопросы по теме