「Rustの基礎 - コレクション型」の版間の差分
編集の要約なし |
|||
| 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: []