Доступ к типу компонента кортежной структуры

Я хочу использовать структуру кортежа в качестве ключа 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();
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
0
34
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

В 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 разрешаются статически.

Извини. Надо было уточнить, что структура кортежа определена и принадлежит ящику, а не мне. Обновил вопрос.

Crend King 06.04.2022 03:16

Справедливо. Смотрите редактирование в моем посте, если это работает для вас.

Silvio Mayolo 06.04.2022 03:23

Спасибо. Может быть, я (или кто-то другой) мог бы написать макрос для упрощения шаблона.

Crend King 06.04.2022 08:31

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