Как создать UnsafeCellбезопасно? - PullRequest
0 голосов
/ 02 октября 2018

В документации UnsafeCell написано

Тип UnsafeCell<T> является единственным допустимым способом получения псевдонимов, которые считаются изменяемыми.

Единственный метод построения:

pub const fn new(value: T) -> UnsafeCell<T>

Однако невозможно создать c_void, мы можем создать только *mut c_void или *const c_void.

можно создать UnsafeCell<c_void> из *mut c_void?При этом мы можем сообщить компилятору, что указатель может указывать на что-то изменяемое.

Или это не обязательно?Можем ли мы всегда использовать *mut c_void, даже если мы знаем, что какой-то вызов FFI изменяет данные, на которые он указывает, и у нас есть несколько ссылок на него?

Вариант использования:

struct FFIStruct { v: UnsafeCell<c_void>, other_fields: ... }
impl FFIStruct {
    // We don't want to require &mut self, as we 
    // are sure private call_ffi() will always be called 
    // sequentially, and we don't want to stop
    // status() being callable during the call
    fn call_ffi(&self){ ffi_function(self.v.get()) }
    pub fn status(&self) -> FFIStatus { ... }
}

Теперькак мы создаем FFIStruct?Или просто использовать *mut c_void будет в порядке?

Пример кода для создания &Cell<c_void>

Требуется #![feature(as_cell)]:

unsafe fn get_cell<'a>(p: *mut c_void) -> &'a Cell<c_void> {
    Cell::from_mut(&mut *p)
}

Ответы [ 2 ]

0 голосов
/ 05 октября 2018

После некоторой внутренней дискуссии на форуме Rust и дискуссии по RFC 1861 , я понимаю, c_void - это просто обычный обходной путь, и существуют другие варианты, такие как struct Opaque<UnsafeCell<()>>.

Итак, я пришел к выводу, что мне нужно здесь *const UnsafeCell<c_void>.Из его типа мы знаем:

  • Это необработанный указатель, поэтому он подходит для немедленной отправки в FFI;
  • Необработанный указатель предполагает const, что означает любое приведение к *mut T будет УБ и программист должен избегать этого.Это защищает память, которую указывает на изменение в Rust (если только через UnsafeCell, причины);
  • Содержит UnsafeCell, поэтому подразумевает внутреннюю изменчивость.Это оправдывает возможность функции FFI изменять ее;
  • Невозможно создать c_void, как для UnsafeCell<c_void>.Они могут быть созданы только с помощью FFI.

Demostrate :

use std::cell::UnsafeCell;
use std::os::raw::c_void;

//Let's say all those functions are in an FFI module,
//with the exact same behaviour
fn ffi_create() -> *mut c_void {
    Box::into_raw(Box::new(0u8)) as *mut c_void
}
unsafe fn ffi_write_u8(p: *mut c_void, v:u8) {
    *(p as *mut u8) = v;
}
unsafe fn ffi_read_u8(p: *mut c_void) -> u8 {
    *(p as *mut u8)
}

fn main() {
    unsafe {
        //let's ignore ffi_destroy() for now
        let pointer = ffi_create() as *const UnsafeCell<c_void>;
        let ref_pointer = &pointer;        
        ffi_write_u8((&*pointer).get(), 7);
        let integer = ffi_read_u8((&**ref_pointer).get());
        assert_eq!(integer, 7);
    }
}

Интересно, как легко и эргономично (но выразительно) конвертировать между *mut c_void и *const UnsafeCell<c_void>.

0 голосов
/ 03 октября 2018

TL; DR: просто используйте *mut Foo.Ячейки любого типа не необходимы здесь.


Отказ от ответственности: пока нет формальной модели памяти Rust.

Вы не может создать этот тип, точка, потому что вы не можете 1 создать экземпляр c_void.

Дело в том, что вам не нужно создать такой тип. Псевдоним не пространственный, а временный .Вы можете иметь несколько *mut T, указывающих на одно и то же место, и не имеет значения , пока вы не попытаетесь получить доступ к одному .Это, по сути, преобразует его в ссылку, и требования псевдонимов необходимо поддерживать, пока эта ссылка существует.

необработанные указатели выходят за пределы безопасной модели памяти Rust.

- The Rustonomicon

В отличие от ссылок и умных указателей, необработанные указатели:

  • Разрешено игнорировать правила заимствованияналичие как неизменяемых, так и изменяемых указателей или нескольких изменяемых указателей в одном и том же месте
  • Не гарантируется указание на допустимую память
  • Допускается иметь значение null
  • Не реализовыватьлюбая автоматическая очистка

¸— Язык программирования Rust

См. также:

1 Технически можно , но это только из-за ограничений реализации и обратной совместимости.

...