# Rustの所有権と借用:完全ガイド > Rustの所有権と借用システムをマスターします。所有権ルール、参照、ライフタイム、高度なメモリ管理パターンを解説します。 - Published: 2026-02-20 - Updated: 2026-04-29 - Author: SharpSkill - Tags: rust, ownership, borrowing, memory management, systems programming - Reading time: 18 min --- 所有権システムは、Rustを他のあらゆるプログラミング言語と区別する特徴です。この独自のアプローチは、ガベージコレクタなしでメモリ安全性を保証し、実行時ではなくコンパイル時にバグを検出します。この詳細なガイドでは、所有権と借用のメカニズムを基礎から本番環境向けの高度なパターンまで解説します。 > **Rustの哲学** > > Rustコンパイラは厳格なプログラミングアシスタントとして機能します。コンパイル時にブロックされる所有権エラーは、本番環境で防がれた潜在的なバグを意味します。 ## 所有権の3つの基本ルール 所有権システムは、シンプルでありながら厳格な3つのルールに基づいています。これらを身につければ、Rustのメンタルモデルは自然で予測可能なものになります。 ```rust // ownership_rules.rs // Demonstration of the three fundamental rules fn main() { // Rule 1: Each value has exactly ONE owner let s1 = String::from("hello"); // s1 is the sole owner // Rule 2: There can only be one owner at a time let s2 = s1; // Ownership transferred (moved) from s1 to s2 // println!("{}", s1); // Compile ERROR: s1 no longer exists println!("s2 = {}", s2); // Only s2 is valid now // Rule 3: When the owner goes out of scope, the value is dropped { let s3 = String::from("temporary"); println!("s3 inside block = {}", s3); } // s3 is automatically freed here (drop is called) // println!("{}", s3); // ERROR: s3 no longer exists } ``` これら3つのルールは、use-after-free、double-free、メモリリークといったバグのカテゴリ全体を排除します。コンパイラはこれらのルールが守られていることを静的に検証します。 ## MoveとCopy:転送セマンティクスの理解 代入の振る舞いはデータ型に依存します。Copyトレイトを実装する型は複製され、それ以外の型はmoveによって転送されます。 ```rust // move_vs_copy.rs // Distinction between Copy types and Move types fn main() { // Copy types: values stored on the stack, known size let x: i32 = 42; let y = x; // x is COPIED, not moved println!("x = {}, y = {}", x, y); // Both are valid // Other Copy types: f64, bool, char, tuples of Copy types let point = (3.0, 4.0); let point_copy = point; // Tuple copy println!("Original: {:?}, Copy: {:?}", point, point_copy); // Move types: values on the heap, dynamic size let s1 = String::from("owned"); let s2 = s1; // s1 is MOVED to s2 // println!("{}", s1); // ERROR: value moved println!("s2 = {}", s2); // Vec, HashMap, Box are also Move types let vec1 = vec![1, 2, 3]; let vec2 = vec1; // Move, not copy // println!("{:?}", vec1); // ERROR println!("vec2 = {:?}", vec2); } // Explicit clone to duplicate Move types fn explicit_clone() { let original = String::from("important data"); let clone = original.clone(); // Explicit duplication (memory cost) println!("Original: {}", original); // Still valid println!("Clone: {}", clone); // Independent copy } ``` Move/Copyの区別は基本的なものです。代入が所有権を転送するのか、独立したコピーを作成するのかを決定します。 > **Cloneを使うタイミング** > > `.clone()`の呼び出しは意図的であるべきです。cloneだらけのコードは設計上の問題を示しているかもしれません。借用がより良い解決策となることが多いです。 ## 借用:不変参照と可変参照 借用により、所有権を取得することなく値にアクセスできます。このメカニズムにより、Rustコードは安全かつ高性能になります。 ```rust // borrowing_basics.rs // Immutable and mutable references fn main() { let s = String::from("hello"); // Immutable reference: read-only, multiple allowed let len = calculate_length(&s); // Immutable borrow println!("'{}' has {} characters", s, len); // s still valid // Multiple simultaneous immutable references: OK let r1 = &s; let r2 = &s; let r3 = &s; println!("r1={}, r2={}, r3={}", r1, r2, r3); } fn calculate_length(s: &String) -> usize { // s is a reference, not the owner s.len() } // s goes out of scope but doesn't drop anything (not owner) // Mutable references: modification allowed fn mutable_borrowing() { let mut s = String::from("hello"); change(&mut s); // Mutable borrow println!("After modification: {}", s); } fn change(s: &mut String) { s.push_str(", world!"); // Modification via mutable reference } ``` 借用の黄金ルール:複数の不変参照、または1つの可変参照のいずれかであり、両者が同時に存在することは決してありません。 ## ボローチェッカーのルール ボローチェッカーは借用ルールを検証するコンパイラのコンポーネントです。そのエラーを理解することで、問題を素早く解決できます。 ```rust // borrow_checker_rules.rs // Strict borrow checker rules fn main() { // RULE 1: No mutable reference with immutable references let mut s = String::from("hello"); let r1 = &s; // Immutable reference: OK let r2 = &s; // Another immutable reference: OK // let r3 = &mut s; // ERROR: cannot borrow as mutable println!("{} and {}", r1, r2); // AFTER using r1 and r2, they are "dead" let r3 = &mut s; // Now OK: r1 and r2 no longer used r3.push_str(" world"); println!("{}", r3); // RULE 2: Only one mutable reference at a time let mut data = String::from("exclusive"); let ref1 = &mut data; // let ref2 = &mut data; // ERROR: already borrowed mutably ref1.push_str("!"); println!("{}", ref1); } // RULE 3: References cannot outlive the data fn dangling_reference_prevented() { let reference; { let s = String::from("short-lived"); // reference = &s; // ERROR: s doesn't live long enough } // s is dropped here, reference would be invalid // Solution: move the value out of the scope let owned_outside; { let s = String::from("moved out"); owned_outside = s; // Move, not reference } println!("{}", owned_outside); // OK: owned_outside is the owner } ``` ボローチェッカーはNon-Lexical Lifetimes (NLL)を使用します。参照は最後に使用された時点までアクティブとみなされ、スコープの終わりまでではありません。 ## ライフタイム:参照の有効期間を注釈する ライフタイムは、参照が有効であり続けることをコンパイラが検証するのを助ける注釈です。ほとんどの場合、自動的に推論されます。 ```rust // lifetimes_explained.rs // Explicit lifetime annotations // Without annotation: compiler infers lifetimes fn first_word(s: &str) -> &str { match s.find(' ') { Some(i) => &s[..i], None => s, } } // With explicit annotation: same function fn first_word_explicit<'a>(s: &'a str) -> &'a str { // 'a means: returned reference lives as long as the input match s.find(' ') { Some(i) => &s[..i], None => s, } } // When annotations are necessary: multiple references fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { // Compiler cannot guess which reference is returned if x.len() > y.len() { x } else { y } } fn main() { let string1 = String::from("long string"); let result; { let string2 = String::from("xyz"); result = longest(&string1, &string2); println!("Longest: {}", result); // OK here } // println!("{}", result); // ERROR if uncommented: string2 dropped } ``` ライフタイムはデータが生きる期間を変更するものではなく、異なる参照のライフタイム間の関係を記述します。 ## 構造体におけるライフタイム 構造体が参照を含む場合、構造体が参照先のデータより長く生存しないことを保証するため、ライフタイムを注釈する必要があります。 ```rust // struct_lifetimes.rs // Structs containing references // Struct with reference: lifetime required struct ImportantExcerpt<'a> { part: &'a str, // This reference must live at least as long as the struct } impl<'a> ImportantExcerpt<'a> { // Method returning a reference with the same lifetime fn level(&self) -> i32 { 3 } // Elision rule: &self implies the output lifetime fn announce_and_return_part(&self, announcement: &str) -> &str { println!("Attention: {}", announcement); self.part // Returns with 'a lifetime from self } } fn main() { let novel = String::from("Call me Ishmael. Some years ago..."); let first_sentence = novel.split('.').next().unwrap(); let excerpt = ImportantExcerpt { part: first_sentence, // OK: novel outlives excerpt }; println!("Excerpt: {}", excerpt.part); println!("Level: {}", excerpt.level()); } // Static lifetime: reference valid for the entire program duration fn static_lifetime_example() { let s: &'static str = "This string is in the binary"; // String literals always have 'static lifetime println!("{}", s); } ``` ライフタイム省略のルールにより、一般的なケースでは注釈を省略でき、コードが読みやすくなります。 ## 高度なパターン:内部可変性 可変性をコンパイル時ではなく実行時に検証する必要がある場合があります。Rustはこのパターンに対応する型を提供しています:RefCellとCellです。 ```rust // interior_mutability.rs // Interior mutability with RefCell and Cell use std::cell::{Cell, RefCell}; // Cell: for Copy types, replaces the entire value struct Counter { count: Cell, // Mutable despite &self } impl Counter { fn new() -> Counter { Counter { count: Cell::new(0) } } fn increment(&self) { // Modification via immutable reference! self.count.set(self.count.get() + 1); } fn get(&self) -> u32 { self.count.get() } } // RefCell: for non-Copy types, checks at runtime struct CachedValue { value: RefCell>, } impl CachedValue { fn new() -> CachedValue { CachedValue { value: RefCell::new(None) } } fn get_or_compute(&self, compute: impl FnOnce() -> String) -> String { // borrow() for reading, borrow_mut() for writing if self.value.borrow().is_none() { *self.value.borrow_mut() = Some(compute()); } self.value.borrow().as_ref().unwrap().clone() } } fn main() { let counter = Counter::new(); counter.increment(); counter.increment(); println!("Counter: {}", counter.get()); // 2 let cache = CachedValue::new(); let result = cache.get_or_compute(|| { println!("Expensive computation..."); String::from("result") }); println!("Value: {}", result); // Second call: no recomputation let result2 = cache.get_or_compute(|| String::from("never executed")); println!("Cache hit: {}", result2); } ``` RefCellとCellは借用検証を実行時に移します。ルール違反はコンパイルエラーではなくpanicを引き起こします。 > **Panicに注意** > > `RefCell::borrow_mut()`は、値がすでに借用されている場合panicします。明示的なエラー処理には`try_borrow_mut()`を使うとよいでしょう。 ## スマートポインタと所有権 Box、Rc、Arcなどのスマートポインタは、特定のユースケースに対して異なる所有権戦略を提供します。 ```rust // smart_pointers.rs // Box, Rc, and Arc for different ownership patterns use std::rc::Rc; use std::sync::Arc; use std::thread; // Box: single owner, data on the heap fn box_example() { let boxed = Box::new(vec![1, 2, 3, 4, 5]); println!("Boxed vec: {:?}", boxed); // Useful for: recursive types, large objects, trait objects } // Rc: reference counting, multiple owners (single-thread) fn rc_example() { let data = Rc::new(String::from("shared data")); let clone1 = Rc::clone(&data); // Increments the counter let clone2 = Rc::clone(&data); println!("Count: {}", Rc::strong_count(&data)); // 3 println!("All share: {}, {}, {}", data, clone1, clone2); } // Freed when counter reaches 0 // Arc: thread-safe Rc (Atomic Reference Counting) fn arc_example() { let data = Arc::new(vec![1, 2, 3]); let handles: Vec<_> = (0..3).map(|i| { let data_clone = Arc::clone(&data); thread::spawn(move || { println!("Thread {}: {:?}", i, data_clone); }) }).collect(); for handle in handles { handle.join().unwrap(); } } fn main() { box_example(); rc_example(); arc_example(); } ``` スマートポインタの選択は所有権パターンに依存します:単一(Box)、シングルスレッドでの共有(Rc)、マルチスレッドでの共有(Arc)です。 ## 実用的な所有権パターン 所有権システムを中心にコードを構造化するための一般的なパターンを示します。 ```rust // ownership_patterns.rs // Practical patterns for ownership management // Pattern 1: Builder pattern with chained ownership struct RequestBuilder { url: String, headers: Vec<(String, String)>, timeout: Option, } impl RequestBuilder { fn new(url: &str) -> Self { RequestBuilder { url: url.to_string(), headers: Vec::new(), timeout: None, } } // Consumes self and returns the new self fn header(mut self, key: &str, value: &str) -> Self { self.headers.push((key.to_string(), value.to_string())); self // Returns ownership } fn timeout(mut self, seconds: u64) -> Self { self.timeout = Some(seconds); self } fn build(self) -> Request { Request { url: self.url, headers: self.headers, timeout: self.timeout.unwrap_or(30), } } } struct Request { url: String, headers: Vec<(String, String)>, timeout: u64, } // Pattern 2: Cow (Copy-on-Write) to avoid allocations use std::borrow::Cow; fn process_text(input: &str) -> Cow { if input.contains("REPLACE") { // Allocation only if modification needed Cow::Owned(input.replace("REPLACE", "NEW")) } else { // No allocation, returns a reference Cow::Borrowed(input) } } // Pattern 3: Take to extract from an Option fn extract_value(data: &mut Option) -> String { data.take().unwrap_or_else(|| String::from("default")) // take() replaces with None and returns ownership of the value } fn main() { // Builder pattern let request = RequestBuilder::new("https://api.example.com") .header("Authorization", "Bearer token") .header("Content-Type", "application/json") .timeout(60) .build(); println!("URL: {}, Timeout: {}s", request.url, request.timeout); // Cow pattern let text1 = process_text("hello world"); // No allocation let text2 = process_text("hello REPLACE"); // Allocation println!("{} | {}", text1, text2); // Take pattern let mut optional = Some(String::from("extracted")); let value = extract_value(&mut optional); println!("Value: {}, Option: {:?}", value, optional); // None } ``` これらのパターンは所有権システムを活用して、人間工学的で高性能なAPIを作成します。 ## まとめ Rustの所有権と借用システムは、メモリ管理におけるパラダイムシフトを表しています。一度マスターすれば、高性能で安全なコードを書くための強力な味方となります。 **重要なポイント:** ✅ 所有権の3つのルール:単一の所有者、所有権の転送、自動drop ✅ 借用:複数の不変参照、または1つの排他的可変参照 ✅ ライフタイム:参照のライフタイム間の関係を注釈する ✅ 内部可変性:実行時に検証される可変性のためのRefCellとCell ✅ スマートポインタ:Box(単一)、Rc(共有)、Arc(thread-safe) ✅ 実用的なパターン:イディオマティックなAPIのためのBuilder、Cow、Take ボローチェッカーは最初は厳しく感じるかもしれませんが、報告される各エラーは回避された潜在的なバグを表します。実践を通じて、所有権の観点で考えることが自然になり、すべての言語におけるコード品質が向上します。 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ja/blog/rust/ownership-borrowing-rust-complete-guide