mcpField
R
rust-unsafe

rust-unsafe

github.com/mohitmishra786開発269568 回インストール18 週間前に更新
リポジトリ

rust-unsafe について

rust-unsafe は、unsafe Rust の作成、レビュー、検討をエージェント向けに支援します。不変条件、安全な抽象化、ポインター規則、監査項目を説明します。

主な機能

  1. 01unsafe の5つの機能
  2. 02生ポインターの安全規則
  3. 03unsafe を包む安全な抽象化
  4. 04transmute と UnsafeCell の指針
  5. 05unsafe 監査チェックリスト

活用シーン

  1. 01unsafe ブロックのレビュー
  2. 02生ポインター向け安全なラッパーの設計
  3. 03unsafe trait 実装の確認
  4. 04unsafe が必要かどうかの判断

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mohitmishra786/low-level-dev-skills --skill rust-unsafe

インストール後、Claude Code で/rust-unsafeを入力して読み込まれたか確認します。

同梱ファイルreferences/unsafe-patterns.md

詳細

Rust unsafe

目的

安全でない Rust の記述、レビュー、検討をエージェントが行うためのガイドです。unsafe が必要な操作、安全な抽象化の作成方法、監査のパターン、よくある落とし穴、unsafe を使うべき場面を説明します。

トリガー

  • 「Rust で unsafe を使う必要があるのはどんなときですか?」
  • 「安全でないコードを安全な抽象化で包むにはどうすればよいですか?」
  • 「unsafe ブロックを監査するにはどうすればよいですか?」
  • 「Rust の生ポインターにはどのようなルールがありますか?」
  • 「transmute は何をするもので、どのような場合に安全ですか?」
  • 「UnsafeCell を正しく実装するにはどうすればよいですか?」

ワークフロー

1. 5 つの unsafe スーパー能力

unsafe によって、安全な Rust では利用できない次の 5 つの機能が使えるようになります。

  1. 生ポインターをデリファレンスする(*const T、*mut T)
  2. 安全でない関数を呼び出す(extern "C" 関数を含む)
  3. 可変な静的変数にアクセスまたは変更する
  4. unsafe トレイトを実装する(Send、Sync)
  5. ユニオンのフィールドにアクセスする

メモリ割り当て、借用、クロージャを含む Rust のその他すべての操作は、unsafe ブロック内でも安全なルールに従います。

2. 生ポインター
// Creating raw pointers (safe — no dereference yet)
let x = 42u32;
let ptr: *const u32 = &x;
let mut_ptr: *mut u32 = &mut some_val as *mut u32;

// Null pointer
let null: *const u32 = std::ptr::null();
let null_mut: *mut u32 = std::ptr::null_mut();

// Dereference (unsafe)
let val = unsafe { *ptr };

// Null check
if !ptr.is_null() {
    let val = unsafe { *ptr };
}

// Offset (safe to compute, unsafe to dereference)
let arr = [1u32, 2, 3, 4, 5];
let p = arr.as_ptr();
let third = unsafe { *p.add(2) };   // arr[2]
let also_third = unsafe { *p.offset(2) };

// Slice from raw parts
let slice: &[u32] = unsafe {
    std::slice::from_raw_parts(p, arr.len())
};

生ポインターを安全にデリファレンスするためのルール:

  • ポインターが null でないこと
  • ポインターが T に対して正しくアラインされていること
  • メモリが T として初期化されていること
  • エイリアシングのルールに違反していないこと(各場所に対する &mut は 1 つだけ)
  • メモリが参照のライフタイム全体にわたって有効であること
3. unsafe 関数とトレイト
// Declare unsafe function (callers must uphold invariants)
/// # Safety
/// `ptr` must be non-null and aligned to `T`, and point to initialized data.
/// The caller must ensure no other mutable reference to the same location exists.
unsafe fn read_ptr<T>(ptr: *const T) -> T {
    ptr.read()  // ptr::read is unsafe
}

// Call unsafe function
let val = unsafe { read_ptr(some_ptr) };

// Unsafe trait — implementor must uphold safety invariants
unsafe trait MyUnsafeTrait {
    fn operation(&self);
}

// Implementing an unsafe trait is unsafe
unsafe impl MyUnsafeTrait for MyType {
    fn operation(&self) { /* must uphold the trait's invariants */ }
}

// Send and Sync
// Send: type can be moved to another thread
// Sync: type can be shared between threads (&T is Send)
unsafe impl Send for MyType {}
unsafe impl Sync for MyType {}
4. unsafe を使った安全な抽象化
// The golden rule: unsafe blocks should be small, isolated, and
// wrapped in a safe API that maintains the invariant

pub struct MyVec<T> {
    ptr: *mut T,
    len: usize,
    cap: usize,
}

impl<T> MyVec<T> {
    pub fn new() -> Self {
        MyVec { ptr: std::ptr::NonNull::dangling().as_ptr(), len: 0, cap: 0 }
    }

    // Safe public API
    pub fn get(&self, index: usize) -> Option<&T> {
        if index < self.len {
            // Safety: index < len guarantees ptr+index is in bounds and initialized
            Some(unsafe { &*self.ptr.add(index) })
        } else {
            None
        }
    }

    // # Safety comment documents the invariant
    pub fn push(&mut self, val: T) {
        if self.len == self.cap {
            self.grow();
        }
        // Safety: len < cap after grow(), so ptr+len is in bounds
        unsafe { self.ptr.add(self.len).write(val) };
        self.len += 1;
    }
}

// Implement Drop to clean up
impl<T> Drop for MyVec<T> {
    fn drop(&mut self) {
        // Safety: ptr was allocated with this layout, and all elements are initialized
        unsafe {
            std::ptr::drop_in_place(std::slice::from_raw_parts_mut(self.ptr, self.len));
            std::alloc::dealloc(self.ptr as *mut u8,
                std::alloc::Layout::array::<T>(self.cap).unwrap());
        }
    }
}
5. transmute
// transmute: reinterpret bits of one type as another
// Both types must have the same size

// Safe uses:
let x: u32 = 0x3f800000;
let f: f32 = unsafe { std::mem::transmute(x) };  // bits → float

// Transmute slice pointer (sound if types have same size/align)
let bytes: &[u8] = &[0x00, 0x00, 0x80, 0x3f];
let floats: &[f32] = unsafe {
    std::slice::from_raw_parts(bytes.as_ptr() as *const f32, 1)
};

// Prefer safe alternatives when available:
let f = f32::from_bits(x);         // instead of transmute for float bits
let n = u32::from_ne_bytes(bytes); // instead of transmute for byte arrays

transmute でよくある落とし穴:

  • サイズが異なる(コンパイルエラーになりますが、ジェネリック型では確認してください)
  • 無効な列挙型の値を作成する
  • ライフタイムが不適切な参照を作成する
6. UnsafeCell — 内部可変性
use std::cell::UnsafeCell;

// UnsafeCell is the only way to mutate through a shared reference
struct MyCell<T> {
    value: UnsafeCell<T>,
}

impl<T: Copy> MyCell<T> {
    fn new(val: T) -> Self {
        MyCell { value: UnsafeCell::new(val) }
    }

    fn get(&self) -> T {
        // Safety: single-threaded, no concurrent mutation
        unsafe { *self.value.get() }
    }

    fn set(&self, val: T) {
        // Safety: single-threaded, no outstanding references
        unsafe { *self.value.get() = val }
    }
}
7. unsafe の監査チェックリスト

unsafe ブロックをレビューする際は、次の点を確認します。

  • 不変条件を説明する // Safety: コメントがありますか?
  • 生ポインターは null ではありませんか?
  • 生ポインターは対象の型に対して正しくアラインされていますか?
  • メモリは初期化されていますか?
  • 参照のライフタイムは有効ですか?
  • エイリアシングのルールは守られていますか(& と &mut を同時に使用していませんか)?
  • extern "C" の場合、C の不変条件は文書化され、検証されていますか?
  • Send/Sync の実装では、スレッドセーフ性が実際に保証されていますか?
  • unsafe ブロックは可能な限り小さくなっていますか?
  • unsafe コードに対する Miri のテストがありますか?
8. unsafe を使う場面
Before reaching for unsafe, check:
├── Does std have a safe API? (Vec, Box, Arc — usually yes)
├── Does a crate handle it? (memmap2, nix, windows-sys)
├── Can you restructure to avoid it?
└── Is the performance gain measured and significant?

Legitimate uses:
├── FFI to C libraries (extern "C")
├── OS-level APIs (syscalls, mmap, ioctl)
├── Performance-critical data structures (custom allocators, SoA)
├── Hardware access (embedded, drivers)
└── Implementing safe abstractions (the standard library itself)

unsafe のパターンと監査の例については、references/unsafe-patterns.md を参照してください。

関連スキル

  • skills/rust/rust-sanitizers-miri を使用してください。Miri は unsafe コードのテストに欠かせないツールです。
  • FFI のコンテキストにおける unsafe のパターンには、skills/rust/rust-ffi を使用してください。
  • unsafe コードで発生するパニックのデバッグには、skills/rust/rust-debugging を使用してください。
  • unsafe におけるエイリアシングとメモリ順序には、skills/low-level-programming/memory-model を使用してください。

コメント