Как я могу инициализировать sigset_t или другие переменные, используемые в качестве выходных параметров в Rust?

Я пытаюсь узнать больше о FFI в Rust и связях с библиотеками C, в частности libc. Во время своих поисков я столкнулся со следующей проблемой.

Нормальный шаблон в C

void(* sig_set(int sig, void(*handler)(int))) {
    // uninitialized sigaction structs
    struct sigaction new_action, old_action;

    // assign options to new action
    new_action.sa_flags = SA_RESTART;
    new_action.sa_handler = handler;
    sigemptyset(&new_action.sa_mask);

    if(sigaction(sig, &new_action, &old_action) < 0) {
        fprintf(stderr, "Error: %s!\n", "signal error");
        exit(1);
    }
    return old_action.sa_handler;
}

Попытка в ржавчине

use libc; // 0.2.77

fn sig_init(sig: i32, handler: fn(i32) -> ()) -> usize {
    unsafe {
        let mut new_action: libc::sigaction;
        let mut old_action: libc::sigaction;

        new_action.sa_flags = 0x10000000;
        new_action.sa_sigaction = handler as usize;
        libc::sigemptyset(&mut new_action.sa_mask as *mut libc::sigset_t);

        libc::sigaction(
            sig,
            &mut new_action as *mut libc::sigaction,
            &mut old_action as *mut libc::sigaction,
        );
        old_action.sa_sigaction
    }
}

Компилятор выдаст следующую ошибку:

error[E0381]: assign to part of possibly-uninitialized variable: `new_action`
 --> src/lib.rs:8:9
  |
8 |         new_action.sa_flags = 0x10000000;
  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of possibly-uninitialized `new_action`

error[E0381]: borrow of possibly-uninitialized variable: `old_action`
  --> src/lib.rs:15:13
   |
15 |             &mut old_action as *mut libc::sigaction,
   |             ^^^^^^^^^^^^^^^ use of possibly-uninitialized `old_action`

Это имеет смысл, так как могут произойти очень плохие вещи, если sigemptyset будет читать из sa_mask. Итак, я попробовал следующее в строке 3 выше.

let mut new_action: libc::sigaction = libc::sigaction {
    sa_sigaction: handler as usize,
    sa_flags: 0x10000000,
    sa_mask: mask,
};

Это не сработает, так как в приведенном выше примере отсутствует _restorer, а _restorer является закрытым. Как мне обойти эту проблему или подобную ситуацию? Вы бы использовали что-то вроде mem::transmute?


person Daniel Robertson    schedule 20.12.2015    source источник


Ответы (2)


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

Modern Rust в большинстве случаев предлагает использовать MaybeUninit. Применительно к вашему случаю это будет выглядеть примерно так:

use std::mem::MaybeUninit;

let mut new_action: libc::sigaction = MaybeUninit::zeroed().assume_init();
let mut old_action: libc::sigaction = MaybeUninit::zeroed().assume_init();

MaybeUninit был стабилизирован в Rust 1.36. До этого вы могли использовать std::mem::uninitialized(), что дает вам неинициализированное значение. LLVM будет считать содержимое неопределенным и будет выполнять агрессивную оптимизацию на основе этого. Вы должны инициализировать любое значение, прежде чем оно будет прочитано.

В вашем случае больше подходит std::mem::zeroed(), который дает вам значение, память которого заполнена нулями. Это функция unsafe, потому что такое значение не обязательно допустимо для всех типов. zeroed() подходит для типов простых старых данных (POD). Применительно к вашему случаю это будет выглядеть примерно так:

use std::mem;

let mut new_action: libc::sigaction = mem::zeroed();
let mut old_action: libc::sigaction = mem::zeroed();
person Francis Gagné    schedule 20.12.2015

Как насчет использования mem::zeroed? В документах даже говорится:

Иногда это полезно для функций FFI, но в целом его следует избегать.

person John Zwinck    schedule 20.12.2015