「Rustの基礎 - コレクション型」の版間の差分

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
ナビゲーションに移動 検索に移動
編集の要約なし
21行目: 21行目:
* 優先度管理
* 優先度管理
*: BinaryHeap<T>
*: BinaryHeap<T>
* ビット集合
*: BitSet (外部クレート)
<br><br>
<br><br>


251行目: 253行目:
  // 出力
  // 出力
  [0, 1, 2]
  [0, 1, 2]
</syntaxhighlight>
<br><br>
== BTreeMap::range() : 範囲検索 ==
BTreeMap<K, V>では、range()メソッドを使用して、指定した範囲のキーに対応する要素を効率的に取得できる。<br>
<br>
範囲検索が必要な場合に使用する。<br>
<syntaxhighlight lang="rust">
use std::collections::BTreeMap;
fn main() {
    let mut map = BTreeMap::new();
    map.insert(1, "one");
    map.insert(2, "two");
    map.insert(3, "three");
    map.insert(4, "four");
    map.insert(5, "five");
    // 2から4までの範囲を取得
    for (key, value) in map.range(2..=4) {
      println!("{}: {}", key, value);
    }
}
// 出力
2: two
3: three
4: four
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
<br><br>
283行目: 314行目:
  // 出力
  // 出力
  hello
  hello
</syntaxhighlight>
<br><br>
== 配列とスライス ==
==== [T; N] : 固定長配列 ====
固定長配列は、コンパイル時にサイズが決まっている配列型である。<br>
<br>
サイズが固定されている場合に使用し、スタック上に格納される。<br>
<syntaxhighlight lang="rust">
fn main() {
    let arr: [i32; 5] = [1, 2, 3, 4, 5];
    println!("{:?}", arr);
    println!("Length: {}", arr.len());
    // 同じ値で初期化
    let arr2 = [0; 10];
    println!("{:?}", arr2);
}
// 出力
[1, 2, 3, 4, 5]
Length: 5
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
</syntaxhighlight>
<br>
==== &[T] : スライス ====
スライスは、配列やベクタの一部への参照である。<br>
<br>
データをコピーせずに部分的にアクセスしたい場合に使用する。<br>
<syntaxhighlight lang="rust">
fn main() {
    let arr = [1, 2, 3, 4, 5];
    let slice = &arr[1..4];
    println!("{:?}", slice);
    let v = vec![10, 20, 30, 40, 50];
    let slice2 = &v[..3];
    println!("{:?}", slice2);
}
// 出力
[2, 3, 4]
[10, 20, 30]
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
<br><br>
342行目: 416行目:
  Result: 5
  Result: 5
  Error: division by zero
  Error: division by zero
</syntaxhighlight>
<br><br>
== Cell<T>とRefCell<T> : 内部可変性 ==
==== Cell<T> : Copyな型の内部可変性 ====
Cell<T>は、不変参照から値を変更できるようにする型である。<br>
<br>
Copyトレイトを実装する型(i32、bool型等)に対して使用する。<br>
<syntaxhighlight lang="rust">
use std::cell::Cell;
fn main() {
    let c = Cell::new(5);
    println!("{}", c.get());
    c.set(10);
    println!("{}", c.get());
}
// 出力
5
10
</syntaxhighlight>
<br>
==== RefCell<T> : 動的な借用チェック ====
RefCell<T>は、実行時に借用ルールをチェックする型である。<br>
<br>
コンパイル時に借用チェックができない場合に使用する。<br>
<syntaxhighlight lang="rust">
use std::cell::RefCell;
fn main() {
    let c = RefCell::new(vec![1, 2, 3]);
    // 不変借用
    {
      let borrowed = c.borrow();
      println!("{:?}", borrowed);
    }
    // 可変借用
    {
      let mut borrowed_mut = c.borrow_mut();
      borrowed_mut.push(4);
    }
    println!("{:?}", c.borrow());
}
// 出力
[1, 2, 3]
[1, 2, 3, 4]
</syntaxhighlight>
<br><br>
== Rc<T>とArc<T> : 参照カウント ==
==== Rc<T> : シングルスレッド用の参照カウント ====
Rc<T>は、複数の所有者を持つデータを表現する型である。<br>
<br>
単一スレッド内で複数の所有者が必要な場合に使用する。<br>
<syntaxhighlight lang="rust">
use std::rc::Rc;
fn main() {
    let a = Rc::new(5);
    let b = Rc::clone(&a);
    let c = Rc::clone(&a);
    println!("a: {}, count: {}", a, Rc::strong_count(&a));
    println!("b: {}, count: {}", b, Rc::strong_count(&b));
    println!("c: {}, count: {}", c, Rc::strong_count(&c));
}
// 出力
a: 5, count: 3
b: 5, count: 3
c: 5, count: 3
</syntaxhighlight>
<br>
==== Arc<T> : マルチスレッド用の参照カウント ====
Arc<T>は、スレッド間で共有できる参照カウント型である。<br>
<br>
マルチスレッド環境で複数の所有者が必要な場合に使用する。<br>
<syntaxhighlight lang="rust">
use std::sync::Arc;
use std::thread;
fn main() {
    let data = Arc::new(vec![1, 2, 3, 4, 5]);
    let handles: Vec<_> = (0..3)
      .map(|i| {
          let data = Arc::clone(&data);
          thread::spawn(move || {
            println!("Thread {}: {:?}", i, data);
          })
      })
      .collect();
    for handle in handles {
      handle.join().unwrap();
    }
}
// 出力
Thread 0: [1, 2, 3, 4, 5]
Thread 1: [1, 2, 3, 4, 5]
Thread 2: [1, 2, 3, 4, 5]
</syntaxhighlight>
<br><br>
== Box<T> : ヒープ割り当て ==
Box<T>は、値をヒープ上に格納するスマートポインタである。<br>
<br>
再帰的なデータ構造や、サイズが大きい値をヒープに格納したい場合に使用する。<br>
<syntaxhighlight lang="rust">
fn main() {
    let b = Box::new(5);
    println!("b = {}", b);
    // 大きなデータをヒープに格納
    let large_data = Box::new([0; 1000]);
    println!("Length: {}", large_data.len());
}
// 出力
b = 5
Length: 1000
</syntaxhighlight>
<br><br>
== Cow<T> : Clone on Write ==
Cow<T>は、必要になるまでクローンを遅延させる型である。<br>
<br>
読み取り専用の場合はクローンせず、変更が必要な場合のみクローンする。<br>
<syntaxhighlight lang="rust">
use std::borrow::Cow;
fn main() {
    let s: Cow<str> = Cow::Borrowed("hello");
    println!("{}", s);
    let mut s2: Cow<str> = Cow::Borrowed("hello");
    s2.to_mut().push_str(" world");
    println!("{}", s2);
}
// 出力
hello
hello world
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
<br><br>

2025年11月30日 (日) 02:45時点における版

概要

コレクション型は、複数のデータを効率的に管理するためのデータ構造である。

Rustのコレクション型は、ヒープ上にデータを格納し、コンパイル時にサイズが決まっていなくても使用できる。
用途に応じて適切なコレクション型を選択することで、効率的なプログラムを作成することができる。


コレクション型の選択

用途に応じて適切なコレクション型を選択することが重要である。

  • 順序付きリスト
    Vec<T>
  • キー・値の検索
    HashMap<K, V>
  • 重複排除
    HashSet<T>
  • 両端操作
    VecDeque<T>
  • ソート済み
    BTreeMap<K, V> / BTreeSet<T>
  • 優先度管理
    BinaryHeap<T>
  • ビット集合
    BitSet (外部クレート)



Vec<T> : 可変長配列

Vec<T>は、動的にサイズが変わる配列型である。
連続したメモリ領域に要素を格納し、インデックスによる高速なアクセスが可能である。
順序付きリストが必要な場合に使用する。

 fn main() {
    let mut v = vec![1, 2, 3];
    v.push(4);
    v.push(5);
 
    println!("{:?}", v);
 
    // インデックスでアクセス
    println!("{}", v[0]);
 
    // 要素の削除
    v.pop();
    println!("{:?}", v);
 }
 
 // 出力
 [1, 2, 3, 4, 5]
 1
 [1, 2, 3, 4]



HashMap<K, V> : ハッシュマップ

HashMap<K, V>は、キーと値のペアを格納するコレクション型である。

キーを指定することで、対応する値を高速に検索できる。
キーと値の検索が必要な場合に使用する。

 use std::collections::HashMap;
 
 fn main() {
    let mut scores = HashMap::new();
 
    scores.insert("Blue", 10);
    scores.insert("Red", 50);
 
    // 値の取得
    let score = scores.get("Blue");
    println!("{:?}", score);
 
    // キーが存在するか確認
    if scores.contains_key("Blue") {
       println!("Blue team exists");
    }
 
    // 全要素の走査
    for (key, value) in &scores {
       println!("{}: {}", key, value);
    }
 }
 
 // 出力
 Some(10)
 Blue team exists
 Blue: 10
 Red: 50



HashSet<T> : ハッシュセット

HashSet<T>は、重複のない値の集合を表すコレクション型である。

値の存在確認が高速に行うことができるる。
重複を排除する場合に使用する。

 use std::collections::HashSet;
 
 fn main() {
    let mut set = HashSet::new();
 
    set.insert(1);
    set.insert(2);
    set.insert(2); // 重複は無視される
 
    println!("{:?}", set);
 
    // 値の存在確認
    if set.contains(&1) {
       println!("1 is in the set");
    }
 
    // 要素数
    println!("Size: {}", set.len());
 }
 
 // 出力
 {1, 2}
 1 is in the set
 Size: 2



VecDeque<T> : 両端キュー

VecDeque<T>は、先頭と末尾の両方から効率的に要素を追加・削除できるコレクション型である。

両端操作が頻繁に必要な場合に使用する。

 use std::collections::VecDeque;
 
 fn main() {
    let mut deque = VecDeque::new();
 
    // 末尾に追加
    deque.push_back(1);
    deque.push_back(2);
 
    // 先頭に追加
    deque.push_front(0);
 
    println!("{:?}", deque);
 
    // 先頭から削除
    deque.pop_front();
 
    // 末尾から削除
    deque.pop_back();
 
    println!("{:?}", deque);
 }
 
 // 出力
 [0, 1, 2]
 [1]



BTreeMap<K, V> / BTreeSet<T> : ソート済みマップ・セット

BTreeMap<K, V>とBTreeSet<T>は、キーがソートされた状態で保持されるコレクション型である。

順序付きの走査が必要な場合や、範囲検索を行いたい場合に使用する。

 use std::collections::BTreeMap;
 
 fn main() {
    let mut map = BTreeMap::new();
 
    map.insert(3, "three");
    map.insert(1, "one");
    map.insert(2, "two");
 
    // キーがソートされた順で出力される
    for (key, value) in &map {
       println!("{}: {}", key, value);
    }
 }
 
 // 出力
 1: one
 2: two
 3: three


 use std::collections::BTreeSet;
 
 fn main() {
    let mut set = BTreeSet::new();
 
    set.insert(5);
    set.insert(1);
    set.insert(3);
 
    // ソートされた順で出力される
    for value in &set {
       println!("{}", value);
    }
 }
 
 // 出力
 1
 3
 5



BinaryHeap<T> : 優先度キュー

BinaryHeap<T>は、最大値を効率的に取り出せる優先度キューである。

優先度管理が必要な場合に使用する。

 use std::collections::BinaryHeap;
 
 fn main() {
    let mut heap = BinaryHeap::new();
 
    heap.push(3);
    heap.push(1);
    heap.push(5);
    heap.push(2);
 
    // 最大値から順に取り出される
    while let Some(value) = heap.pop() {
       println!("{}", value);
    }
 }
 
 // 出力
 5
 3
 2
 1



LinkedList<T> : 連結リスト

LinkedList<T>は、双方向連結リストである。

実際の使用では、Vec<T>の方が効率的なことが多いため、あまり使用されない。

 use std::collections::LinkedList;
 
 fn main() {
    let mut list = LinkedList::new();
 
    list.push_back(1);
    list.push_back(2);
    list.push_front(0);
 
    println!("{:?}", list);
 }
 
 // 出力
 [0, 1, 2]



BTreeMap::range() : 範囲検索

BTreeMap<K, V>では、range()メソッドを使用して、指定した範囲のキーに対応する要素を効率的に取得できる。

範囲検索が必要な場合に使用する。

 use std::collections::BTreeMap;
 
 fn main() {
    let mut map = BTreeMap::new();
 
    map.insert(1, "one");
    map.insert(2, "two");
    map.insert(3, "three");
    map.insert(4, "four");
    map.insert(5, "five");
 
    // 2から4までの範囲を取得
    for (key, value) in map.range(2..=4) {
       println!("{}: {}", key, value);
    }
 }
 
 // 出力
 2: two
 3: three
 4: four



文字列型

String : 所有権を持つ文字列

Stringは、所有権を持つ可変長の文字列型である。

文字列の内容を変更する必要がある場合に使用する。

 fn main() {
    let mut s = String::from("hello");
    s.push_str(" world");
 
    println!("{}", s);
 }
 
 // 出力
 hello world


&str : 文字列スライス

&strは、文字列への参照を表す型である。

文字列を読み取るだけで変更しない場合に使用する。

 fn main() {
    let s: &str = "hello";
    println!("{}", s);
 }
 
 // 出力
 hello



配列とスライス

[T; N] : 固定長配列

固定長配列は、コンパイル時にサイズが決まっている配列型である。

サイズが固定されている場合に使用し、スタック上に格納される。

 fn main() {
    let arr: [i32; 5] = [1, 2, 3, 4, 5];
    println!("{:?}", arr);
    println!("Length: {}", arr.len());
 
    // 同じ値で初期化
    let arr2 = [0; 10];
    println!("{:?}", arr2);
 }
 
 // 出力
 [1, 2, 3, 4, 5]
 Length: 5
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]


&[T] : スライス

スライスは、配列やベクタの一部への参照である。

データをコピーせずに部分的にアクセスしたい場合に使用する。

 fn main() {
    let arr = [1, 2, 3, 4, 5];
    let slice = &arr[1..4];
    println!("{:?}", slice);
 
    let v = vec![10, 20, 30, 40, 50];
    let slice2 = &v[..3];
    println!("{:?}", slice2);
 }
 
 // 出力
 [2, 3, 4]
 [10, 20, 30]



Option型とResult型

Option<T> : 値があるかないか

Option<T>は、値が存在するかしないかを表す型である。

Some(value)で値がある状態、Noneで値がない状態を表す。

 fn main() {
    let some_number = Some(5);
    let no_number: Option<i32> = None;
 
    match some_number {
       Some(n) => println!("Number: {}", n),
       None => println!("No number"),
    }
 
    match no_number {
       Some(n) => println!("Number: {}", n),
       None => println!("No number"),
    }
 }
 
 // 出力
 Number: 5
 No number


Result<T, E> : 成功または失敗

Result<T, E>は、処理の成功または失敗を表す型である。

Ok(value)で成功、Err(error)で失敗を表す。
これは、エラー処理が必要な場合に使用する。

 fn divide(a: i32, b: i32) -> Result<i32, String> {
    if b == 0 {
       Err(String::from("division by zero"))
    }
    else {
       Ok(a / b)
    }
 }
 
 fn main() {
    match divide(10, 2) {
       Ok(result) => println!("Result: {}", result),
       Err(e) => println!("Error: {}", e),
    }
 
    match divide(10, 0) {
       Ok(result) => println!("Result: {}", result),
       Err(e) => println!("Error: {}", e),
    }
 }
 
 // 出力
 Result: 5
 Error: division by zero



Cell<T>とRefCell<T> : 内部可変性

Cell<T> : Copyな型の内部可変性

Cell<T>は、不変参照から値を変更できるようにする型である。

Copyトレイトを実装する型(i32、bool型等)に対して使用する。

 use std::cell::Cell;
 
 fn main() {
    let c = Cell::new(5);
    println!("{}", c.get());
 
    c.set(10);
    println!("{}", c.get());
 }
 
 // 出力
 5
 10


RefCell<T> : 動的な借用チェック

RefCell<T>は、実行時に借用ルールをチェックする型である。

コンパイル時に借用チェックができない場合に使用する。

 use std::cell::RefCell;
 
 fn main() {
    let c = RefCell::new(vec![1, 2, 3]);
 
    // 不変借用
    {
       let borrowed = c.borrow();
       println!("{:?}", borrowed);
    }
 
    // 可変借用
    {
       let mut borrowed_mut = c.borrow_mut();
       borrowed_mut.push(4);
    }
 
    println!("{:?}", c.borrow());
 }
 
 // 出力
 [1, 2, 3]
 [1, 2, 3, 4]



Rc<T>とArc<T> : 参照カウント

Rc<T> : シングルスレッド用の参照カウント

Rc<T>は、複数の所有者を持つデータを表現する型である。

単一スレッド内で複数の所有者が必要な場合に使用する。

 use std::rc::Rc;
 
 fn main() {
    let a = Rc::new(5);
    let b = Rc::clone(&a);
    let c = Rc::clone(&a);
 
    println!("a: {}, count: {}", a, Rc::strong_count(&a));
    println!("b: {}, count: {}", b, Rc::strong_count(&b));
    println!("c: {}, count: {}", c, Rc::strong_count(&c));
 }
 
 // 出力
 a: 5, count: 3
 b: 5, count: 3
 c: 5, count: 3


Arc<T> : マルチスレッド用の参照カウント

Arc<T>は、スレッド間で共有できる参照カウント型である。

マルチスレッド環境で複数の所有者が必要な場合に使用する。

 use std::sync::Arc;
 use std::thread;
 
 fn main() {
    let data = Arc::new(vec![1, 2, 3, 4, 5]);
 
    let handles: Vec<_> = (0..3)
       .map(|i| {
          let data = Arc::clone(&data);
          thread::spawn(move || {
             println!("Thread {}: {:?}", i, data);
          })
       })
       .collect();
 
    for handle in handles {
       handle.join().unwrap();
    }
 }
 
 // 出力
 Thread 0: [1, 2, 3, 4, 5]
 Thread 1: [1, 2, 3, 4, 5]
 Thread 2: [1, 2, 3, 4, 5]



Box<T> : ヒープ割り当て

Box<T>は、値をヒープ上に格納するスマートポインタである。

再帰的なデータ構造や、サイズが大きい値をヒープに格納したい場合に使用する。

 fn main() {
    let b = Box::new(5);
    println!("b = {}", b);
 
    // 大きなデータをヒープに格納
    let large_data = Box::new([0; 1000]);
    println!("Length: {}", large_data.len());
 }
 
 // 出力
 b = 5
 Length: 1000



Cow<T> : Clone on Write

Cow<T>は、必要になるまでクローンを遅延させる型である。

読み取り専用の場合はクローンせず、変更が必要な場合のみクローンする。

 use std::borrow::Cow;
 
 fn main() {
    let s: Cow<str> = Cow::Borrowed("hello");
    println!("{}", s);
 
    let mut s2: Cow<str> = Cow::Borrowed("hello");
    s2.to_mut().push_str(" world");
    println!("{}", s2);
 }
 
 // 出力
 hello
 hello world



共通のメソッド

lenメソッドで要素数を取得する

多くのコレクション型は、lenメソッドで要素数を取得することができる。

 fn main() {
    let v = vec![1, 2, 3, 4, 5];
    println!("Length: {}", v.len());
 }
 
 // 出力
 Length: 5


is_emptyメソッドで空かどうか確認する

コレクションが空かどうかを確認するには、is_empty メソッドを使用する。

 fn main() {
    let v: Vec<i32> = Vec::new();
    println!("Is empty: {}", v.is_empty());
 
    let v2 = vec![1, 2, 3];
    println!("Is empty: {}", v2.is_empty());
 }
 
 // 出力
 Is empty: true
 Is empty: false


clear関数で全要素を削除する

コレクションの全要素を削除するには、clear メソッドを使用する。

 fn main() {
    let mut v = vec![1, 2, 3, 4, 5];
    println!("Before: {:?}", v);
 
    v.clear();
    println!("After: {:?}", v);
 }
 
 // 出力
 Before: [1, 2, 3, 4, 5]
 After: []