💡

Rustからlibc呼ぶときのエラーハンドリングがどうなってるか調べてみた

2023/12/23に公開

はじめに

Rustはシステムプログラミング言語なので、libc経由でシステムコールを呼ぶことが多いと思うのですが、C言語はエラーハンドリングの仕方がRustとは全く異なっていて返り値でエラーを判定してerrnoから取得するというやり方をしています。
じゃあ、Rustでエラーハンドリングするときはどうするの?というのを調べてみたのがこの記事です。

RustからOSのエラーを取得する方法

std::io::Errorlast_os_errorというのが用意されていて、これで取得することができます。
https://doc.rust-lang.org/std/io/struct.Error.html#method.last_os_error

例えばこんな感じで使えます。

use std::io::Error;

let os_error = Error::last_os_error();
println!("last OS error: {os_error:?}");

エラーハンドリングを行う方法

unsafeブロック内で返り値で判定し、last_os_errorで取得します。
例えばこんな感じです。

let ret = unsafe { libc::flock(file.as_raw_fd(), operation) };
if ret == -1 { Err(io::Error::last_os_error()) } else { Ok(Lock { _file: file }) }

実際に例えばここでそういう実装がされています。

同じ様な処理を何度も書くのが嫌なのか、共通化されている箇所もあります。
https://github.com/rust-lang/rust/blob/c03d978a4bcb7c01d8cdf80bd7600b27e2d21588/library/std/src/sys/unix/mod.rs#L317

macroを使って複数の値に一度にimplするのは時々見る手法ですね。

macro_rules! impl_is_minus_one {
    ($($t:ident)*) => ($(impl IsMinusOne for $t {
        fn is_minus_one(&self) -> bool {
            *self == -1
        }
    })*)
}

おわりに

面白そうな話題だと思ったのですが、そんな大したことなかった…

参考

Discussion