Предисловие: У меня есть некоторый опыт работы с Rust, но не очень, поэтому возможно, что есть тривиальное решение моей проблемы. В частности, мне все еще не очень хорошо с тем, как Rust обрабатывает память, хотя я понимаю базовую концепцию c.
Во всяком случае, я использую внешний ящик (winapi, если быть точным, но более об этом позже), что накладывает некоторые ограничения на то, что я пытаюсь сделать. Вот факты:
- Мне нужно вызвать ящик, чтобы создать что-то, для чего мне затем предоставляется дескриптор.
- Я хочу сохранить дескриптор вместе с другими соответствующими данные в структуре.
- Ящик также требует обратного вызова, который он затем часто вызывает.
- Одним из параметров функции обратного вызова, вызываемой ящиком, является дескриптор.
- В функции обратного вызова я также хотел бы получить доступ к своим собственным данным, хранящимся в структуре, вместе с дескриптором, поэтому я должен иметь возможность каким-то образом получить структуру из дескриптора. .
- Ящик не дает хорошего способа предоставить ссылку на мою структуру для функции обратного вызова. В результате я храню глобальную структуру дескриптора ha sh map, но она не работает, как я надеюсь.
Вот некоторый код, который примерно демонстрирует мою проблему:
use std::cell::RefCell;
use std::collections::HashMap;
thread_local!(static WRAPPER_LIST_CELL: RefCell<HashMap<u32, usize>> = RefCell::new(HashMap::new()));
static mut MAX_ID: u32 = 0;
struct Data {
x: i32
}
fn main() {
let data = Data { x: 17 };
let wrapper= create_wrapper(data);
// For all intents and purposes, do_something below is actually called by
// the external crate in a loop. This ought to demonstrate the problem
// well enough though.
do_something(wrapper.id);
}
struct Wrapper {
id: u32,
data: Data,
}
impl Wrapper {
fn new(data: Data) -> Wrapper {
// This is the function that would actually call the crate to
// create the 'something' I need.
unsafe {
// No ID is actually needed when using the crate. The crate
// provides a unique handle instead, which works largely to
// the same effect as the ID here.
let id = MAX_ID;
MAX_ID += 1;
let wrapper = Wrapper { id: id, data: data };
WRAPPER_LIST_CELL.with(|cell| {
let mut list = cell.borrow_mut();
list.insert(wrapper.id, &wrapper as *const Wrapper as usize);
});
wrapper
}
}
}
fn create_wrapper(data: Data) -> Wrapper {
Wrapper::new(data)
}
fn do_something(id: u32) {
unsafe {
WRAPPER_LIST_CELL.with(|cell| {
let list_ptr = cell.as_ptr();
let list_ptr = (*view_list_ptr)[&id] as *const Wrapper;
println!("do_something: {}", (*list_ptr).data.x); // not 17
});
}
}
По большей части все выглядит хорошо. Однако , когда я пытаюсь получить доступ к моей структуре в do_something, я замечаю, что ее адрес отличается от того, что есть в main. Это приводит к тому, что я пытаюсь получить доступ к неправильному расположению в памяти, что, вероятно, является причиной сбоя программы.
Ящик действительно обеспечивает способ свободного хранения и доступа к указателю на пользовательские данные. Например, моя структура, но он принимает указатель по существу как isize, то есть адрес памяти моей структуры, который, опять же, изменяется между main и do_something. То есть не похоже, что его действительно можно использовать.
Как уже упоминалось, я использую ящик winapi, то есть привязки Rust для Windows API. В случае, если это уместно, я получаю (window) дескрипторы, используя CreateWindowExW, и do_something более или менее WindowPro c. Для простоты использования я хочу обернуть дескриптор окна вместе со своими собственными данными окна в структуру Window. В WindowPro c есть вещи, которые мне нужно сделать, чтобы получить доступ к своим собственным данным окна. Я предоставил упрощенную версию моего реального кода, потому что сама проблема, похоже, может возникнуть и в других ситуациях, и потому что использование Windows API довольно грязно и совсем не относится к вопросу. По запросу я могу предоставить реальный код, используя Windows API.
Наконец, актуальный вопрос: Что я могу сделать, чтобы решить мою проблему, то есть правильно обернуть вещи в структуру и заставить все работать ? Я полагаю, что возможность хранить фактические ссылки в глобальной переменной поможет, но это кажется невозможным, если я что-то упустил.