TGViewer
Блог* Блог* @dereference_pointer_there · 1.98K subscribers
Post #6991 994
#prog #rust #моё

Опытные Rust-программисты знают, что в Rust можно сделать мапу, которая индексируется не рантайм-ключами, а типами. Сделать это довольно просто поверх обычной мапы: достаточно хранить значения в виде Box<dyn Any>, а в качестве ключей использовать TypeId (хотя и есть немного неочевидный нюанс). В геттере мы конструируем TypeId от переданного типа, индексируем им мапу, и если значение есть, то приводим к нужному типа через downcast_ref/downcast_mut.

#[derive(Default)]
struct TypeMap(HashMap<TypeId, Box<dyn Any>>);

impl TypeMap {
fn insert<T: 'static>(&mut self, val: T) {
self.0.insert(TypeId::of::<T>(), Box::new(val));
}

fn get<T: 'static>(&self) -> Option<&T> {
self.0.get(&TypeId::of::<T>())?.downcast_ref::<T>()
}
}

fn main() {
let mut map = TypeMap::default();
map.insert(42_i32);
assert_eq!(map.get::<i32>(), Some(&42));
}


С дополнительными обёртками можно делать мапы с отображениями из T в тип, параметризованный T:

T -> Option<T>
T
-> [T; 2]
T
-> Vec<T>

В общем, принцип понятен. Но что делать, если мы хотим хранить в параметризованной типами мапе коллбеки, связанные с типом?

Пусть для определённости мы хотим хранить коллбеки вида FnMut(&mut T). Как я уже говорил, в Rust нет общего типа для замыканий одной сигнатуры — есть лишь общий трейт. Для того, чтобы сохранить замыкание в TypeMap, нам потребуется каким-то образом стереть тип дважды: сначала от конкретного типа замыкания до dyn FnMut(&mut T), а потом от него до dyn Any.

Наиболее простой способ сделать это — забоксить замыкание дважды:

// определение `TypeMap` выше

#[derive(Default)]
struct CallbackMap(TypeMap);

impl CallbackMap {
fn insert<T: 'static>(&mut self, f: impl FnMut(&mut T) + 'static) {
let boxed = Box::new(f) as Box<dyn FnMut(&mut T) + 'static>;
self.0.insert(boxed);
}

fn get_by_arg<T: 'static>(&mut self) -> Option<&mut (dyn FnMut(&mut T) + 'static)> {
self.0.get_mut::<Box<dyn FnMut(&mut T) + 'static>>().map(|f| &mut **f)
}
}

fn main() {
let mut map = CallbackMap::default();
let multiplier = 7;
let f = move |x: &mut i32| *x *= multiplier;
map.insert(f);
let mut x = 6;
map.get_by_arg().unwrap()(&mut x);
assert_eq!(x, 42);
}
doc.rust-lang.org Any in std::any - Rust A trait to emulate dynamic typing.
  • 👍 7
  • ❤ 2
  • 🤔 2
More from @dereference_pointer_there
  1. Oct 7, 2026#meme про jab
  2. Oct 7, 2026"Famous car"
  3. Oct 7, 2026photo post
  4. Oct 7, 2026Во избежание оффтопа под постом о поиске работы срач дискуссию о криптовалютах обсуждайте…
  5. Oct 7, 2026Блог* pinned «Меня зовут Антон, и в настоящий момент я ищу работу. У меня 5 лет опыта в пр…
  6. Oct 7, 2026^Папищеки, обращаюсь к вам за помощью: поделитесь, пожалуйста, особенно если вы знаете, чт…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →