Как я могу создать HashSet из заимствованного массива универсального типа в Rust?

#rust

#Ржавчина

Вопрос:

У меня есть функция, принимающая два заимствованных массива универсального типа T , и я хотел бы создать HashSet s из этих массивов, чтобы я мог их сравнить.

Я думал, что смогу сделать что-то вроде этого:

 pub fn sublist<T: PartialEq>(first_list: amp;[T], second_list: amp;[T]) -> bool {
  let first_set: HashSet<T> = first_list.iter().collect();
  let second_set: HashSet<T> = second_list.iter().collect();

  first_set.is_subset(amp;second_set)
}
  

Но в итоге я получаю следующие ошибки:

 a value of type `std::collections::HashSet<T>` cannot be built from an iterator over elements of type `amp;T`

value of type `std::collections::HashSet<T>` cannot be built from `std::iter::Iterator<Item=amp;T>`

help: the trait `std::iter::FromIterator<amp;T>` is not implemented for `std::collections::HashSet<T>`
  

Из-за первой строки ошибки я подумал, что смогу решить ее следующим образом (я просто изменил тип hashset на ссылки amp;T ):

 pub fn sublist<T: PartialEq>(first_list: amp;[T], second_list: amp;[T]) -> bool {
  let first_set: HashSet<amp;T> = first_list.iter().collect();
  let second_set: HashSet<amp;T> = second_list.iter().collect();

  first_set.is_subset(amp;second_set)
}
  

Но затем я вижу эти ошибки:

 the trait bound `T: std::cmp::Eq` is not satisfied

the trait `std::cmp::Eq` is not implemented for `T`

note: required because of the requirements on the impl of `std::cmp::Eq` for `amp;T`
note: required because of the requirements on the impl of `std::iter::FromIterator<amp;T>` for `std::collections::HashSet<amp;T>`
  

Я не понимаю, как создавать новые структуры данных из ссылок на массив. Проблема заключается в том, что эти массивы заимствованы, или, в конечном счете, проблема связана с PartialEq этим признаком?

Что, если по какой-либо причине я не могу изменить сигнатуру функции, как я могу использовать hashsets для сравнения коллекций?

Комментарии:

1. HashSet требуется Eq , PartialEq недостаточно. У вас также, вероятно, будет ошибка об отсутствии привязки признака Hash , если вы этого еще не сделали.

2. Это означает, что его невозможно использовать HashSet для сравнения этих массивов? Было бы идиоматично выполнять сравнение вручную или есть какая-то другая коллекция, которую я должен использовать?

3. Rust требует, чтобы любая функциональность дженериков была объявлена заранее. Если PartialEq это все, с чем вам нужно работать, то вы не можете использовать какой-либо хешированный или упорядоченный контейнер, чтобы сделать проверку эффективной. Вы могли бы поместить их в a Vec , но если вы даже не можете их отсортировать (требуется Ord ), тогда зачем беспокоиться? Использование .contains непосредственно в цикле — это действительно единственный вариант, который я вижу с заданными ограничениями.

Ответ №1:

Для использования HashSet вашей функции необходимо иметь границы Eq и Hash признака:

 use std::hash::Hash;
use std::collections::HashSet;

pub fn sublist<T: Eq   Hash>(first_list: amp;[T], second_list: amp;[T]) -> bool {
  let first_set: HashSet<amp;T> = first_list.iter().collect();
  let second_set: HashSet<amp;T> = second_list.iter().collect();

  first_set.is_subset(amp;second_set)
}
  

Если вы знаете только, что T это PartialEq так, вы можете реализовать это следующим образом:

 pub fn sublist<T: PartialEq>(first_list: amp;[T], second_list: amp;[T]) -> bool {
    first_list.iter().all(|v| second_list.contains(v))
}
  

Другие варианты включают T: Ord и использование BTreeSet .