rustを学んで⑦

·
#CLI#Rust#Terminal#rustlings

Rustの学習記録:RcとRefCellで混乱した日

今日はRustの所有権システムの応用編、RcRefCellについて勉強しました。正直かなり混乱しましたが、いくつか重要な気づきがあったので記録しておきます。

Rcの理解

Rc<T>(Reference Counted)は、ヒープ上のデータを複数の所有者で共有するための仕組みです。

BoxとRcの違い

  • Box: 所有者は1つだけ。ムーブすると元の変数は使えなくなる
  • Rc: 複数の所有者が同じデータを指せる。参照カウント方式で管理
use std::rc::Rc;

let data = Rc::new(vec![1, 2, 3]);
let data2 = Rc::clone(&data);  // 参照カウントが増えるだけ
let data3 = Rc::clone(&data);

println!("参照カウント: {}", Rc::strong_count(&data));  // 3

重要なのは、Rcだけでは中身を変更できないという点。変更したい場合はRc<RefCell<T>>と組み合わせる必要があります。

Rcの具体的な使用例

最初は「どんな時に使うの?」と疑問でしたが、いくつか納得できる例がありました。

1. 設定やリソースの共有

これが一番イメージしやすかったです。

use std::rc::Rc;

struct AppConfig {
    database_url: String,
    max_connections: usize,
}

let config = Rc::new(AppConfig {
    database_url: "localhost:5432".to_string(),
    max_connections: 100,
});

// 複数のモジュールで同じ設定を参照
let service1 = DatabaseService { config: Rc::clone(&config) };
let service2 = CacheService { config: Rc::clone(&config) };

データ的にも同じヒープから持ってくるのは効率的で、ベストプラクティスなんだろうなと思いました。

2. グラフやツリー構造

複数のノードから同じノードを参照する必要がある場合にも使われます。

struct Node {
    value: i32,
    children: Vec<Rc<Node>>,  // 子ノードへの参照のリスト
}

混乱ポイント

Vec<Rc>の理解

最初はVec<Rc<Node>>が何なのか混乱しました。分解すると:

Vec<Rc<Node>>
│   │  └─ Node構造体
│   └──── Rcでラップされている
└──────── それらのベクタ(可変長配列)

つまり、子ノードへのポインタのリストということです。

Cons(i32, Rc)の再帰的構造

enum List {
    Cons(i32, Rc<List>),  // 値 と 次のリストへの参照
    Nil,                  // リストの終わり
}

この再帰的なデータ構造は、正直まだよくわかりません。無理に今理解しようとする方が間違っている気がするので、基礎を固めてから戻ってくることにしました。

「Node」という言葉の多義性

「Node」という言葉が複数の意味で使われていて混乱しました:

  1. Node(型) = 構造体の設計図
  2. Node { value: 10, ... } = その設計図から作られた実際のデータ(インスタンス)
  3. Rc::new(...) = そのデータをヒープに置いて、参照カウント付きポインタで包む

なのでRc<Node>は「Node型のインスタンスへの、共有可能なポインタ」という理解にたどり着きました。

RefCellについて

RefCellは「Rcの変更可能版」くらいの理解で今は押さえておくことにしました。

正確には:

  • Rc<T> = 複数箇所から共有できるが、変更不可
  • Rc<RefCell<T>> = 複数箇所から共有でき、かつ変更も可能
use std::rc::Rc;
use std::cell::RefCell;

let data = Rc::new(RefCell::new(5));
let data2 = Rc::clone(&data);

*data.borrow_mut() += 1;  // 変更できる
println!("{}", data2.borrow());  // 6

「共有したい + 変更もしたい = Rc<RefCell<T>>」と覚えておきます。

トレイト(trait)についての疑問

YouTubeでThe Bookの15.5を見ていて、新たな混乱が発生しました。

impl Messenger for MockMessenger {
    fn send(&self, message: &str) {
        self.sent_messages.push(String::from(message));
    }
}

講師が「このトレイトは〜」と説明するのですが、画面にはimplしか映っていない。「え?トレイトってtraitのことじゃないの?」と混乱しました。

トレイトとは

トレイトは**「このメソッドを持っていますよ」という約束・契約**です。他の言語でいうインターフェースに近いです。

// トレイトの定義(契約書)
trait Messenger {
    fn send(&self, message: &str);
}

// トレイトの実装(インプル)
impl Messenger for MockMessenger {
    fn send(&self, message: &str) {
        // 実装
    }
}

整理:

  • trait = トレイトを定義するキーワード
  • impl Trait for Type = トレイトを**実装(インプル)**する構文

講師が「このトレイトは〜」と言うのは、「この(Messengerという)トレイトは〜」という省略した言い方だったようです。

用語について思ったこと

プログラミング用語は日本語に無理やり訳すより、カタカナや英語のままの方が圧倒的にわかりやすいです。

  • impl (インプル) = implementation(実装)
  • trait (トレイト) = 特性、特質 ← 日本語だと意味不明
  • method (メソッド) = 方法、手法 ← これも微妙

Rustのドキュメントもコードも全部英語なので、カタカナや英語で覚えた方が絶対いいと思いました。

循環参照

The Bookの最後の方で循環参照について学びましたが、正直さっぱりでした。

  • RcRefCellの組み合わせ
  • 再帰的なデータ構造
  • Weakポインタ

とか、難しい概念が一気に出てきて、めちゃめちゃ眠くなりました😴

今覚えておくこと:

  • 「循環参照っていう問題がある」
  • Weakで解決できる」

くらいで十分だと判断。必要になったときに戻ってきます。

まとめ

今日の学び:

  • Rcは複数箇所から同じデータを参照したいときに使う
  • 設定の共有のような使い方が実用的でイメージしやすい
  • Rc<RefCell<T>>で共有 + 変更が可能になる
  • 再帰的データ構造は今は無理に理解しなくてOK
  • 用語はカタカナ・英語で覚える方がいい

再帰的なデータ構造や循環参照は、基礎を固めてから改めて勉強することにします。焦らず、一歩ずつ進んでいきます!

次のステップ:Bashを学ぶ

ここまでRustを勉強してきて、正直お腹いっぱいになってきました。特にRcRefCell、循環参照といった難しい概念が続いて、頭が飽和状態です。

そこで、次の一週間はThe Complete Bash Scripting CourseでBashについて学ぶことにしました。

なぜBashを学ぶのか?

  1. 頭の切り替え目的
    Rustの難しい部分で詰まっているときに、全く違うことを学ぶのはリフレッシュになると信じている。

  2. 実用的で即効性の期待
    Rustを書くときもターミナルを使うので、Bashの知識は直接役立ちそうだなと思った。ファイル操作、スクリプト作成など、すぐに使えるスキルをしっかりと学ぶ。

  3. 体系的なコース
    7時間18分の完全なコースで、基礎から応用まで網羅されているみたい。YouTubeで無料で見られるのも魅力的。正し、英語なので気合い入れなきゃなと思っています。

  4. Rustとの相乗効果の期待
    Bashでスクリプトが書けると、Rustのビルドプロセスやテストの自動化に使えます。CLIツールを作るときの理解も深まるはずです。

  5. 一週間という区切り
    「Rustを忘れる」というより「一旦休憩」という感じで、戻ったときに頭がクリアになっている可能性を期待する。

学習計画

次回からのブログは、Bashの学習記録を書いていきます。Rustの学習は中断するのではなく、一旦寝かせておいて、頭がリフレッシュしてから戻ってくる作戦です。

無理に詰め込むより、適度に休憩しながら、長期的に続けられる学習スタイルを目指します!