Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dropを走らせないRustのメモリ管理

Avatar for estie | エスティ estie | エスティ
October 19, 2026
1

 Dropを走らせないRustのメモリ管理

Avatar for estie | エスティ

estie | エスティ

October 19, 2026

More Decks by estie | エスティ

Transcript

  1. 前提 / 所有権とスコープ 所有者がスコープを抜けると drop される • 値には所有者となる変数がいる • 所有者がスコープを抜けると値が

    drop される • 確保していたメモリ領域も解放される • Drop::drop に書いた処理も実行される { let a = String::from("hello"); println!("{a}"); // ここで a が drop → メモリ解放 } // 以降 a にはアクセスできない © 2026 estie Inc. 3
  2. 前提 / メモリリーク Rust はメモリリークがないことを保証していない let file = File::open("foo.txt").unwrap(); mem::forget(file);

    // drop されず、close もされない mem::forget は safe な関数 リークはメモリ安全性の保証の範囲外。Rust はリークに対して寛容 © 2026 estie Inc. 4
  3. Drop を止める API / Box::leak Box::leakでstaticな参照をつくる • &'static mut な参照が返ってくる

    • forget と違い、その後も値を操作できる • 実行中ずっと生存させたいときに便利 • 後でデストラクタを呼びたい用途には不向き • 完全にリークさせる前提の API © 2026 estie Inc. let x = Box::new(41); let r: &'static mut usize = Box::leak(x); // 値は生き続けているので操作できる *r += 1; assert_eq!(*r, 42); 6
  4. Drop を止める API / 生ポインタ 生ポインタを活用するAPI let b = Box::new(String::from("hello"));

    let p: *mut String = Box::into_raw(b); // b は move され、drop されない unsafe { (*p).push_str(", world"); assert_eq!(*p, "hello, world"); drop(Box::from_raw(p)); // Box に戻して drop → 中身もヒープも解放 } safe into_raw で生ポインタ化 © 2026 estie Inc. unsafe ポインタ経由で自由に操作 unsafe from_raw で戻して drop 7
  5. Drop を止める API / 生ポインタ 生ポインタが必要になる代表例:FFI • C などの他言語にはライフタイムや所 有権を伝えられない

    • C 側が持っている間に Rust が drop すると困る • into_raw で所有権ごと手放す • 解放は Rust に戻して行う • from_raw して drop • C の free() では解放しない pub struct Parser { /* ... */ } #[unsafe(no_mangle)] pub extern "C" fn parser_new() -> *mut Parser { Box::into_raw(Box::new(Parser::new())) // C 側へ渡す } #[unsafe(no_mangle)] pub unsafe extern "C" fn parser_free(p: *mut Parser) { if !p.is_null() { drop(unsafe { Box::from_raw(p) }); // Rust で解放 } } // C 側: Parser *p = parser_new(); ... parser_free(p); © 2026 estie Inc. 8
  6. Drop を止める API / ManuallyDrop ManuallyDrop: drop するタイミングを自分で決める • 包んだ値は自動で

    drop されない • into_inner で元に戻すのは safe • ManuallyDrop::drop は unsafe • drop 後も変数自体には触れてしまう • 二重 drop は未定義動作 • drop 後の使用も未定義動作 © 2026 estie Inc. // safe:包む・元に戻す let x = ManuallyDrop::new(Box::new(())); let b: Box<()> = ManuallyDrop::into_inner(x); // unsafe:自分で drop する let mut x = ManuallyDrop::new( String::from("unsafe Rust")); unsafe { ManuallyDrop::drop(&mut x); } println!("{}", *x); // 未定義動作! 9
  7. Drop を止める API / ManuallyDrop 実例:BufWriter::into_parts // Rust 標準ライブラリ BufWriter

    より pub fn into_parts(self) -> (W, Result<Vec<u8>, WriterPanicked>) { let mut this = ManuallyDrop::new(self); // 本体の drop を止める let buf = mem::take(&mut this.buf); let buf = if !this.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) }; // SAFETY: this は drop されないので二重 drop にならない let inner = unsafe { ptr::read(&this.inner) }; (inner, buf) } Drop 実装型のフィールドは個別に move できない → 包んで取り出す © 2026 estie Inc. 10
  8. Drop を止める API / MaybeUninit MaybeUninit:初期化前かもしれない値 • Rustは確保と初期化を同時に行うのが前提 (RAII) •

    確保と初期化をあえて分けるのに使える • フィールドごとの初期化 • 配列の要素の初期値 • 後始末はプログラマの責務 • assume_init struct Config { name: String, port: u16 } let mut cfg = MaybeUninit::<Config>::uninit(); let p = cfg.as_mut_ptr(); let cfg: Config = unsafe { (&raw mut (*p).name).write(String::from("server")); (&raw mut (*p).port).write(8080); cfg.assume_init() }; • assume_init_drop © 2026 estie Inc. 11
  9. まとめ 紹介したAPIの早見表 API 元に戻す方法 解放されるのは unsafe な操作 向いている用途 mem::forget 戻せない

    解放されない なし とにかく drop を止める Box::leak 戻さない前提 解放されない なし 実行中ずっと生かす値 生ポインタ from_raw 元の型に戻して drop した時 戻す・参照外し FFI、自前のデータ構造 ManuallyDrop into_inner(safe) 自分で drop した時 ManuallyDrop::drop フィールドの取り出し MaybeUninit assume_init 初期化後、自分で © 2026 estie Inc. assume_init 系 段階的な初期化 12
  10. まとめ • Rust はメモリリークを保証しない。Drop を止める API がいくつもある • Drop を止めたい理由でAPIを使い分ける

    • ずっと生かしたい → Box::leak • 所有権の外で扱いたい → into_raw / from_raw • drop のタイミングを決めたい → ManuallyDrop • 未初期化の値を扱いたい → MaybeUninit • © 2026 estie Inc. プログラマの責務で自由にできる。メモリ管理は奥が深い! 13