rust - 如何在 Rust 中初始化 sigset_t 或其他用作 "out parameters"的变量?

标签 rust ffi libc out-parameters

我正在努力学习更多关于 Rust 中的 FFI 并链接到 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;
}

尝试使用 Rust

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`

这是有道理的,因为如果 sigemptysetsa_mask 中读取,可能会发生非常糟糕的事情。所以我在上面的第 3 行尝试了以下内容。

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

这将不起作用,因为在上面的示例中缺少 _restorer,但 _restorer 是私有(private)的。我将如何解决这个问题或类似情况?你会使用像 mem::transmute 这样的东西吗?

最佳答案

标准库定义了一些类型和函数来处理初始化。它们是通用的,因此可用于初始化任何类型的值。

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() ,它为您提供了一个值,其存储空间中填满了零。此函数是不安全,因为这样的值不一定对所有类型都合法。 zeroed() 适用于“普通旧数据”(POD) 类型。适用于您的情况,它看起来像:

use std::mem;

let mut new_action: libc::sigaction = mem::zeroed();
let mut old_action: libc::sigaction = mem::zeroed();

关于rust - 如何在 Rust 中初始化 sigset_t 或其他用作 "out parameters"的变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50468294/

相关文章:

rust - Rust 中具有不断变化的行为的有限(游戏)状态机模式?

arrays - 如何克隆长度大于 32 的数组?

rust - Rayon find_any,并返回找到的项目的值

c++ - 到无穷远并返回

rust - 我是否可以在 Rust 的 MacroMatch 中使用逗号以外的分隔符?

haskell - 当在 C 中管理(取消)分配时,Haskell 运行时中的垃圾收集器问题

rust - 如何使用FFI将2D矢量从Rust传递到Fortran?

python - 在什么时候我可以将数组传递回我的 Rust 程序以释放它的内存?

linux - 无法升级 libc6, "Already newest version"

c - 为什么宏在 libc 中可以滥用