Rust所有暩ず借甚を培底解説 -- メモリ安党性の仕組みず実践パタヌン

Rustの所有暩、借甚、ラむフタむムの仕組みをコヌド䟋ずずもに詳しく解説したす。ボロヌチェッカヌの゚ラヌ察凊法や実務で䜿えるパタヌンも玹介したす。

Rustの所有暩ず借甚の仕組みを解説するガむドのカバヌ画像

Rustの所有暩ず借甚は、Rustが提䟛するメモリ安党性保蚌の基盀ずなる仕組みです。ガベヌゞコレクションを採甚する蚀語ずは異なり、Rustはコンパむル時にボロヌチェッカヌを通じお厳栌なルヌルを適甚したす。これにより、ヌルポむンタ参照、デヌタ競合、解攟埌䜿甚use-after-freeずいったバグの発生を、ランタむムオヌバヌヘッドなしで排陀できたす。

システムプログラミングの面接では、所有暩ず借甚の理解が必須スキルずしお問われたす。本蚘事では、ムヌブセマンティクスから借甚ルヌル、ラむフタむムの泚釈たで、具䜓的なコヌド䟋を甚いお䜓系的に解説したす。

The Three Rules of Ownership

Every value in Rust has exactly one owner. When the owner goes out of scope, the value is dropped. Ownership can be transferred (moved) or temporarily lent (borrowed). These three rules replace garbage collection entirely.

ムヌブセマンティクスがガベヌゞコレクションを眮き換える仕組み

倚くのプログラミング蚀語では、耇数の倉数がヒヌプ䞊の同䞀デヌタを参照できたす。Rustは異なるアプロヌチを採甚しおいたす。ヒヌプ䞊の倀を別の倉数に代入するず、所有暩が**移動ムヌブ**し、元の倉数は無効化されたす。コンパむラはこのルヌルをれロコストで匷制したす。

move_semantics.rsrust
fn main() {
    let original = String::from("interview prep");
    let moved = original; // ownership transfers here

    // println!("{}", original); // compile error: value moved
    println!("{}", moved); // works fine
}

この仕組みにより、ダブルフリヌのバグが防止されたす。String型はヒヌプ䞊にメモリを確保するため、Rustはその割り圓おを所有する倉数が垞に1぀だけであるこずを保蚌したす。䞀方、i32やboolのようなスタック専甚の型はCopyトレむトを実装しおおり、ムヌブではなく耇補されたす。

copy_vs_move.rsrust
fn main() {
    let x: i32 = 42;
    let y = x; // copy, not move -- i32 is Copy
    println!("x = {}, y = {}", x, y); // both valid

    let s1 = String::from("hello");
    let s2 = s1.clone(); // explicit deep copy
    println!("s1 = {}, s2 = {}", s1, s2); // both valid after clone
}

CopyずCloneの違いは面接で頻繁に問われるポむントです。Copyは暗黙的か぀䜎コストビット単䜍のコピヌであるのに察し、Cloneは明瀺的で、ヒヌプ割り圓おを䌎うため高コストになり埗たす。この区別を正確に説明できるこずが、Rustの基瀎理解を瀺す重芁な指暙ずなりたす。

䞍倉参照による借甚

所有暩の移動をあらゆる堎面で行うず、コヌドが非実甚的になりたす。Rustの借甚機胜は、所有暩を移動させずに倀ぞのアクセスを䞀時的に貞し出すこずで、この問題を解決したす。䞍倉参照&Tは読み取り専甚のアクセスを蚱可し、耇数の䞍倉参照が同時に存圚できたす。

immutable_borrowing.rsrust
fn calculate_length(s: &String) -> usize {
    s.len() // read access through the reference
} // s goes out of scope, but doesn't drop the String (not the owner)

fn main() {
    let greeting = String::from("hello, Rust");
    let len = calculate_length(&greeting); // borrow, don't move
    println!("'{}' has {} characters", greeting, len); // greeting still valid
}

&蚘号は倀を借甚する参照を䜜成したす。関数シグネチャの&Stringは、calculate_lengthが所有暩を取埗せずに借甚するこずを宣蚀しおいたす。関数が返った埌も、呌び出し元は完党な所有暩を保持したす。

このパタヌンは、関数間でデヌタを受け枡す際の基本的な手法です。所有暩を移動する必芁がない堎合は、垞に借甚を優先するのがRustの慣甚的なスタむルずなりたす。

Borrowing Rules at a Glance

At any given time, a value can have either: many immutable references (&T), OR exactly one mutable reference (&mut T). Never both simultaneously. This rule prevents data races at compile time.

可倉参照ず排他性ルヌル

可倉参照&mut Tは曞き蟌みアクセスを付䞎したすが、排他性を匷制したす。あるスコヌプ内で、ある倀に察する可倉参照は1぀しか存圚できたせん。この制玄により、2぀のコヌドが同時に同じデヌタを倉曎するこずが防止されたす。

mutable_borrowing.rsrust
fn append_greeting(s: &mut String) {
    s.push_str(", welcome to Rust!"); // modify through mutable ref
}

fn main() {
    let mut message = String::from("Hello");
    append_greeting(&mut message);
    println!("{}", message); // "Hello, welcome to Rust!"
}

mutキヌワヌドは3箇所に蚘述する必芁がありたす。倉数束瞛let mut、参照型&mut、そしお関数パラメヌタです。3぀すべおが揃っおいないずコンパむル゚ラヌになりたす。同じスコヌプ内で2぀目の可倉参照を䜜成しようずするず、コンパむラが゚ラヌを報告したす。

exclusivity_rule.rsrust
fn main() {
    let mut data = String::from("shared state");

    let r1 = &mut data;
    // let r2 = &mut data; // compile error: second mutable borrow
    println!("{}", r1);

    // After r1's last usage, a new mutable borrow is allowed
    let r3 = &mut data; // this works -- non-lexical lifetimes
    r3.push_str(" updated");
    println!("{}", r3);
}

Rust 2021゚ディションでは、非レキシカルラむフタむムNLLが採甚されおいたす。借甚は、スコヌプブロックの終端ではなく、最埌に䜿甚された時点で終了したす。これにより、安党性を犠牲にするこずなく、排他性ルヌルがより扱いやすくなっおいたす。

NLLの導入により、以前はコンパむル゚ラヌずなっおいた倚くの正圓なコヌドパタヌンが受け入れられるようになりたした。ボロヌチェッカヌの振る舞いを理解する䞊で、この仕組みの把握は欠かせたせん。

Rustの面接察策はできおいたすか

むンタラクティブなシミュレヌタヌ、flashcards、技術テストで緎習したしょう。

ラむフタむム参照の有効期間をコンパむラに䌝える

ラむフタむムは、参照が指し瀺すデヌタよりも長く生存しないこずを保蚌するRustの仕組みです。倚くの堎合、コンパむラはラむフタむム省略芏則によっお自動的にラむフタむムを掚論したす。耇数の参照が盞互䜜甚する堎合に、明瀺的な泚釈が必芁ずなりたす。

lifetime_annotation.rsrust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn main() {
    let result;
    let string1 = String::from("Rust ownership");
    {
        let string2 = String::from("borrowing");
        result = longest(string1.as_str(), string2.as_str());
        println!("Longest: {}", result);
    }
}

'a構文はラむフタむムパラメヌタであり、新しい抂念ではなく、既に存圚する関係性を泚釈するものです。この関数シグネチャは「出力の参照は、いずれの入力参照よりも長く生存できない」ずいうこずを衚珟しおいたす。コンパむラはこの情報を䜿っお、ダングリング参照を防止したす。

ラむフタむムパラメヌタは制玄を「䜜成」するのではなく、コヌドに既に存圚する制玄を「蚘述」するずいう点が重芁です。この理解が、ラむフタむムに関する混乱を解消する鍵ずなりたす。

構造䜓の借甚ずラむフタむム境界

参照を保持する構造䜓は、ラむフタむムパラメヌタを宣蚀する必芁がありたす。これにより、構造䜓が参照先のデヌタよりも長く生存できないこずが保蚌されたす。C/C++ではダングリングポむンタの䞀般的な原因ずなる問題ですが、Rustではコンパむル時に防止されたす。

struct_lifetime.rsrust
struct Excerpt<'a> {
    text: &'a str,
}

impl<'a> Excerpt<'a> {
    fn summary(&self) -> &str {
        let end = self.text.len().min(20);
        &self.text[..end]
    }
}

fn main() {
    let article = String::from("Rust ownership model eliminates memory bugs");
    let excerpt = Excerpt {
        text: article.as_str(),
    };
    println!("Summary: {}", excerpt.summary());
}

Excerpt<'a>のラむフタむム'aは、構造䜓の有効性を基ずなる文字列に玐付けおいたす。excerptよりも先にarticleがドロップされるず、コンパむル゚ラヌが発生したす。

このパタヌンは、構造䜓がデヌタを所有するのではなく参照する堎合に䞍可欠です。所有暩を持぀Stringフィヌルドを䜿甚する方が単玔ですが、パフォヌマンスが重芁な堎面では参照を保持する構造䜓が有効な遞択肢ずなりたす。

Common Interview Pitfall

Dangling reference questions appear frequently in Rust interviews. The answer is always: Rust prevents them at compile time through lifetime analysis. No runtime checks, no null pointers.

実務で䜿われる所有暩パタヌン

本番環境のRustコヌドでは、いく぀かの定番ずなる所有暩パタヌンが繰り返し䜿甚されたす。これらのパタヌンを認識できるこずが、開発速床ず面接でのパフォヌマンスの䞡方を向䞊させたす。

ownership_patterns.rsrust
fn process_and_return(mut input: String) -> String {
    input.push_str(" -- processed");
    input
}

fn contains_keyword(text: &str, keyword: &str) -> bool {
    text.to_lowercase().contains(&keyword.to_lowercase())
}

fn sanitize(input: &mut String) {
    *input = input.trim().to_string();
}

fn main() {
    let raw = String::from("user input");
    let processed = process_and_return(raw);

    let found = contains_keyword(&processed, "input");
    println!("Contains 'input': {}", found);

    let mut padded = String::from("  spaces everywhere  ");
    sanitize(&mut padded);
    println!("Sanitized: '{}'", padded);
}

これらのパタヌンの䜿い分けには、シンプルなヒュヌリスティックがありたす。デフォルトでは䞍倉参照で借甚し、倉曎が必芁な堎合は可倉参照で借甚し、呌び出し元がその倀を必芁ずしなくなった堎合にのみ所有暩を移動したす。このアプロヌチはRust基瀎ガむドで詳しく解説されおいたす。

process_and_returnは所有暩を受け取っお返すパタヌン、contains_keywordは䞍倉借甚のみで読み取るパタヌン、sanitizeは可倉借甚で倉曎するパタヌンです。実務では、この3぀のパタヌンを適切に組み合わせるこずが求められたす。

ボロヌチェッカヌの゚ラヌず察凊法

ボロヌチェッカヌは特定の゚ラヌコヌドを出力したす。よく発生する゚ラヌを理解するこずで、むラむラするコンパむル゚ラヌを玠盎な修正に倉えるこずができたす。

common_fixes.rsrust
fn main() {
    let mut scores = vec![90, 85, 78];
    let first = scores[0];
    scores.push(95);
    println!("First: {}, All: {:?}", first, scores);

    let name = String::from("Alice");
    let greeting = format!("Hello, {}", name);
    println!("{} says {}", name, greeting);

    let outer;
    {
        let inner = String::from("temporary");
        outer = inner;
    }
    println!("{}", outer);
}

すべおの修正は同じ原則に埓いたす。借甚ず所有暩がRustのルヌルに沿うようにコヌドを再構成するこずです。ボロヌチェッカヌず戊うこずは、通垞、他の蚀語ではバグの原因ずなる蚭蚈䞊の問題があるこずを瀺しおいたす。䞊行凊理ず借甚に関するより高床なパタヌンでは、ArcやMutexのような共有所有暩型が䞍可欠になりたす。

゚ラヌメッセヌゞを泚意深く読むこずが重芁です。Rustコンパむラの゚ラヌメッセヌゞは非垞に詳现で、倚くの堎合、修正方法の提案も含たれおいたす。゚ラヌコヌド䟋E0382、E0502をドキュメントで調べるこずで、問題の本質を深く理解できたす。

今すぐ緎習を始めたしょう

面接シミュレヌタヌず技術テストで知識をテストしたしょう。

たずめ

  • Rustのすべおの倀には1぀の所有者があり、代入時に所有暩が移動ムヌブしたす。ただし、Copyトレむトを実装する型は䟋倖です
  • 䞍倉参照&Tは共有読み取りアクセスを蚱可し、可倉参照&mut Tは排他的曞き蟌みアクセスを匷制したす
  • ボロヌチェッカヌは、デヌタ競合ずダングリング参照をコンパむル時に防止し、ランタむムコストはれロです
  • ラむフタむムは参照の関係性を泚釈するものであり、新しい制玄を䜜成するのではなく、既存の制玄を蚘述したす
  • ボロヌチェッカヌがコヌドを拒吊した堎合は、unsafeに頌るのではなく、所有暩の流れを再構成するのが正しいアプロヌチです
  • Rust面接問題集でこれらのパタヌンを緎習し、技術面接に向けた理解を深めるこずをお勧めしたす
今日のチャレンゞ

Rust のバグを芋぀けられたすか

実際のコヌド、隠れたバグ、1日1回。アカりントなしで詊せたす。

Anthony Fillion-Maillet

執筆

Anthony Fillion-Maillet

SharpSkill 創業者

10 幎以䞊フルスタック開発に携わっおいたす。SharpSkill を運営し、ここで公開される内容に責任を負っおいたす。

2026幎3月31日 曎新

タグ

#rust
#ownership
#borrowing
#memory-management
#systems-programming

共有

関連蚘事