Безопасно ли хранить данные и изменяемый указатель на эти данные в структуре? - PullRequest
0 голосов
/ 12 ноября 2018

Давайте рассмотрим библиотеку оболочки Rust вокруг библиотеки C.Эта библиотека C определяет структуру и использует ее в качестве изменяемого указателя в своем API.

Оболочка Rust определяет следующую структуру со ptr, указывающим на data.

struct Wrapper {
    data: struct_from_c_t,
    ptr: *mut struct_from_c_t,
}

Есливсе использования этого указателя выполняются в течение времени жизни структуры Wrapper, с какими еще потенциальными проблемами я могу столкнуться при использовании этого указателя в небезопасном коде?

Всегда ли разыскивание и использование этого указателя всегда безопасно в этой конструкции?

Для более подробного контекста цель состоит в том, чтобы иметь возможность вызывать функции FFI с использованием этого указателя из функций, заимствуемых Wrapper без возможности изменения.

1 Ответ

0 голосов
/ 12 ноября 2018

Обычно это плохая идея , и она может пойти не так очень легко.

Сначала прочитайте Почему я не могу сохранить значение и ссылку на это значение в одной и той же структуре? для более подробного объяснения того, почему безопасный Rust предотвращает эту конструкцию во время компиляции.

TL; DR, если вы когда-либо переместите структуру Wrapper, указатель будет недействительным. Разыменование это приведет к неопределенному поведению (плохо).

Если вы можете убедиться, что любой из:

  1. Wrapper никогда не перемещается.
  2. ptr обновляется каждый раз, когда вы перемещаете структуру.

Тогда указатель будет действительным и безопасным для разыменования (при условии, что все другие предупреждения о небезопасном коде поддерживаются).


Что еще хуже, нет причины держать указатель на первом месте; Вы можете взять ссылку на значение и преобразовать его в указатель всякий раз, когда вам нужно:

extern "C" {
    fn ffi_fn(data: *mut struct_from_c_t);
}

struct Wrapper {
    data: struct_from_c_t,
}

impl Wrapper {
    fn do_thing(&mut self) {
        unsafe { ffi_fn(&mut self.data) }
    }
}

из функций, заимствованных Wrapper без возможности изменения

Без контекста это кажется сомнительным решением, но у Rust есть инструменты для внутренней изменчивости:

use std::cell::RefCell;

struct Wrapper {
    data: RefCell<struct_from_c_t>,
}

impl Wrapper {
    fn do_thing(&self) {
        unsafe { ffi_fn(&mut *self.data.borrow_mut()) }
    }
}
...