Я хочу использовать структуру кортежа в качестве ключа HashMap. Структура кортежа имеет ровно один компонент — u32. Сама структура не реализует Hash, поэтому ее нельзя напрямую использовать в качестве ключа. Тем не менее, я всегда могу использовать базовый u32 в качестве ключа.
struct St(u32); // defined in a crate not owned by me
let s = St(1);
let mut m = HashMap::<u32, i32>::new();
m.insert(s.0, 2);
Вопрос: есть ли способ вместо жесткого кодирования u32 в объявлении HashMap использовать фактический тип компонента St
, так что, если St
изменит его на что-то вроде isize, он все равно будет работать. Что-то вроде decltype С++:
struct St(isize); // the crate changes this
let mut m = HashMap::<decltype(St.0), i32>::new();
В Rust нет эквивалента C++ decltype
, но вы всегда можете создать псевдоним типа, который будет использоваться в обоих случаях.
type StImpl = u32;
struct St(StImpl);
let mut m = HashMap::<StImpl, i32>::new();
И, конечно же, вы также можете St
просто получить Hash
и Eq
использовать его в хэш-карте в первую очередь.
#[derive(Hash, PartialEq, Eq)]
struct St(isize);
let mut m = HashMap::<St, i32>::new();
Теперь производные impl
будут автоматически обновляться, если вы измените внутренности St
.
В ответ на вопрос редактирования я предложу, что бы я сделал здесь. Если вы действительно хотите защитить свой код от изменений в библиотеке, которой вы не управляете, в будущем, вы можете обернуть St
в пользовательский struct
, чьи Hash
и Eq
делегированы внутренним компонентам. Что-то вроде
use std::hash::{Hash, Hasher};
// Derive Clone, Copy, and anything else useful St implements here.
struct StWrapper(St);
impl Hash for StWrapper {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.0.0.hash(hasher);
}
}
impl PartialEq for StWrapper {
fn eq(&self, rhs: &Self) -> bool {
self.0.0 == rhs.0.0
}
}
impl Eq for StWrapper {}
Все наши реализации ссылаются на внутреннюю часть St
(через self.0.0
), но мы никогда не упоминаем его тип, поэтому, пока внутренний тип является чем-то, что, по крайней мере, дает нам Eq
и Hash
, это будет продолжать работать, даже если конкретный тип изменения. Таким образом, мы можем использовать StWrapper
в качестве типа ключа в HashMap
и быть уверенными, что он защищен от будущих изменений в библиотеке.
Вам решать, гарантирует ли ваш конкретный вариант использования такой шаблонный шаблон для защиты от изменений в другой библиотеке в будущем, или стоит просто использовать u32
и нести расходы, если они действительно изменяют реализации. Обратите внимание, что, хотя этот подход требует приличного кода, он, скорее всего, будет абстракцией с нулевой стоимостью во время выполнения, поскольку St
, StWrapper
и внутренний тип в большинстве случаев должны компилироваться в одно и то же представление (не гарантированно, но вероятно), и все вызовы hash
и eq
разрешаются статически.
Справедливо. Смотрите редактирование в моем посте, если это работает для вас.
Спасибо. Может быть, я (или кто-то другой) мог бы написать макрос для упрощения шаблона.
Извини. Надо было уточнить, что структура кортежа определена и принадлежит ящику, а не мне. Обновил вопрос.