Programmingバックエンド開発者

Rustにおけるスマートポインタ(Box、Rc、Arc、RefCell)はどのように機能しますか?それぞれの違いは何ですか、どのような場合にどれを選ぶべきですか?

Hintsage AIアシスタントで面接を突破

答え

Rustには従来のガベージコレクタがないため、複雑な構造体の所有権を管理するためにスマートポインタを使用します。最も一般的に使用されるものは次のとおりです:

  • Box<T> — ヒープにオブジェクトのメモリを割り当て、その所有権を移動します。データのサイズがコンパイル時に不明な場合や、移動可能だがユニークなリソースが必要な場合に使用されます。

  • Rc<T>(Reference Counted) — 参照カウントを使用し、複数の変数が不変データの所有権を「共有」できるようにします(単一スレッドコンテキストのみ)。

  • Arc<T>(Atomic Reference Counted) — こちらも参照カウントを実装しますが、アトミックです。マルチスレッドプログラムでの使用が許可されています。

  • RefCell<T> — 実行時に「内部可変」所有権を提供し、不変の参照を介してさえ内容を変更できるようにしますが、単一スレッドのみ(スレッドセーフではありません!)。

例:

use std::rc::Rc; let a = Rc::new(vec![1,2,3]); let b = Rc::clone(&a); // 現在、aとbの両方が同じデータの所有者です

引っ掛け問題

すべてのスレッドがデータを読むだけの場合、Rc<T>をマルチスレッドコードで使用できますか?説明してください。

答え:いいえ、できません! Rc<T>はデータへの不変アクセスのみを許可しますが、Rc<T>自体はスレッドセーフではなく、内部の参照カウントがデータレースから保護されていないためです。これにはArc<T>が必要です — その内部カウンタはスレッドセーフです。

例:

// 次のコードはコンパイルされません! use std::thread; use std::rc::Rc; let five = Rc::new(5); for _ in 0..10 { let five = Rc::clone(&five); thread::spawn(move || { println!("{}", five); }); }

知識の欠如による実際のエラーの例


物語

Rc<T>を使用してキャッシュをスレッド間で共有し、ウェブサービスを高速化しようとしました。実際には奇妙なクラッシュとデータの破損が発生しました。調査の結果、Rcはスレッドセーフではなく、参照カウントが破損したことが判明しました。解決策:Arc<T>に置き換えました。

物語

デスクトップアプリケーションで、大きなオブジェクトツリーをBox<T>に保持しましたが、UIの複数の部分がデータの所有権を共有する必要があることを考慮しませんでした。これにより、コンパイルエラーが発生しました。解決策は、アクセスを共有するためにRc<T>を使用することでした。

物語

ビジネスロジックモジュールで、RefCell<T>を使用してデータへの可変アクセスを提供しましたが、これらのデータはArc<T>を介してスレッド間で渡されていました。しかし、RefCell<T>とArc<T>を組み合わせようとした結果、データレースと実行時のパニックが発生しました。スレッドセーフなオプションのためには、RefCell<T>の代わりにMutex<T>またはRwLock<T>を使用する必要がありました。