Open5
Rust メモ
ピン留めされたアイテム
参考記事
const
と static
の違い
const
と static
での変数定義は、Rustにおいて似てはいますが、いくつかの重要な違いがあります。
const
での定義
-
不変:
const
で定義された値は不変です。つまり、その値を変更することはできません。 -
インライン:
const
で定義された値は、それを使用するたびに、コンパイル時にその場所にインライン展開されます。これは、メモリアドレスを持たないということを意味します。 -
ライフタイム:
const
で定義された値は、暗黙的に'static
ライフタイムを持ちます。これは、プログラムの実行期間全体にわたって存在することを意味します。
例:
const PI: f64 = 3.14159265358979323846;
static
での定義
-
不変または可変:
static
キーワードを使用すると、変数は不変(static
)または可変(static mut
)にすることができます。 -
メモリアドレス:
static
変数はプログラムのメモリ空間に固定の場所を持ちます。これは、実際のメモリアドレスを持つことを意味します。 -
ライフタイム:
static
変数も'static
ライフタイムを持ちます。つまり、これらもプログラムの実行期間全体にわたって存在します。
例:
static PI: f64 = 3.14159265358979323846;
比較と違い
-
const
はコンパイル時にインライン展開されるため、メモリ使用量が少ないかもしれませんが、static
はプログラムのメモリ空間に実際の場所を持ちます。 -
static
変数は、アドレスが必要な場合(例えば、安全でないコードでの使用や、特定のメモリアドレスにデータを配置する場合)に使われることがあります。 -
static mut
を使用すると、変数を可変にすることができますが、これは安全でない操作を必要とするため、慎重に使用する必要があります。
したがって、const
と static
は機能的に似ている面がありますが、使用方法や特定のユースケースに応じて選択する必要があります。基本的には、不変の値であれば const
を、メモリアドレスが必要な場合や可変性が必要な場合には static
(または static mut
)を使用します。
Result
とanyhow::Result
の違い
Result
型とanyhow::Result
は、Rustのエラー処理に関連するものだが、それぞれ異なる目的と特性を持っている。
-
標準のResult型:
- 定義: Rustの標準ライブラリに含まれる
Result
型は、Result<T, E>
として定義される。ここで、T
は成功時の型、E
はエラー時の型を表す。 - 用途: 明示的にエラーの型を指定したい場合や、特定のエラー型として定義されたenumやstructを利用したい場合に使用される。
- カスタムエラー:
Result
型を使用する場合、エラーの種類や情報を細かく制御できる。独自のエラータイプを定義して、エラーの理由や追加情報を詳細に示すことができる。
- 定義: Rustの標準ライブラリに含まれる
-
anyhow::Result型:
- 定義:
anyhow::Result<T>
は、anyhow
クレートによって提供される型で、Result<T, anyhow::Error>
として実際に定義される。このanyhow::Error
は、さまざまなエラーのソースからのエラーをキャッチして格納するための型。 - 用途: 主にアプリケーションや実行可能なバイナリで使用されることを目的とする。エラーを詳細に分類することをあまり気にしない場面や、さまざまなエラーソースからのエラーを同じ型として扱いたい場合に便利。
- シンプルなエラーハンドリング:
anyhow::Result
は、エラーハンドリングをシンプルにすることを目的としている。これにより、さまざまなエラータイプを一つのanyhow::Error
タイプで統一的に扱うことができる。
- 定義:
まとめ:
- 標準の
Result
型は、エラーの型を明示的に指定し、エラーハンドリングの詳細を制御する場面で利用される。 -
anyhow::Result
は、さまざまなエラーを一つの型で扱い、エラーハンドリングを簡略化することを目的とする。主にアプリケーションのトップレベルやバイナリレベルでのエラーハンドリングに適している。
Cell
と RefCell
の違い
Cell
とRefCell
はどちらも内部可変性を実現する方法ですが、大きな違いはスレッド安全性の有無です。
1. Cell
- スレッド不安全
- コピー可能
- ライフタイムのチェックが不要
use std::cell::Cell;
fn main() {
let c = Cell::new(5);
let value = c.get();
println!("初期値: {}", value); // 出力: 初期値: 5
c.set(10);
let new_value = c.get();
println!("新しい値: {}", new_value); // 出力: 新しい値: 10
}
この例では、Cell
を使って値を変更しています。Cell
はスレッド不安全なため、複数のスレッドから同時にアクセスすると問題が発生する可能性があります。
2. RefCell
- スレッドセーフではない(スレッド不安全)
- コピー不可能
- ライフタイムのチェックが必要
use std::cell::RefCell;
struct Data {
value: RefCell<i32>,
}
fn main() {
let data = Data { value: RefCell::new(5) };
let mut value = data.value.borrow_mut();
*value = 10;
println!("新しい値: {}", *value); // 出力: 新しい値: 10
}
RefCell
を使う場合は、borrow_mut
メソッドで可変の参照を取得し、その値を変更する必要があります。RefCell
もスレッドセーフではありませんが、ライフタイムのチェックが行われるため、コンパイル時に一部のエラーを検出できます。
RefCell
はスレッド不安全ですが、スレッドセーフでない状況でも安全性を高めることができます。一方で、Cell
はスレッド不安全かつコピー可能なため、単純な用途に適しています。
まとめ
-
Cell
はスレッド不安全だがコピー可能で、ライフタイムチェックが不要 -
RefCell
はスレッド不安全だがコピー不可能で、ライフタイムチェックが必要 - どちらも内部可変性を実現するが、安全性とコピー可能性のトレードオフがある
- 状況に応じて適切なものを選択する必要がある
参考記事
Send と Sync
-
Send
:オブジェクトの所有権を別のスレッドへ移動することを許可しますが、その移動先のスレッドからはデータを変更することはできません。 -
Sync
:オブジェクトへの不変参照を複数のスレッドから共有することを許可しますが、それらのスレッドからデータを変更することはできません。 - 両方のトレイトとも、データの変更が許可されないことで、データ競合を回避しています。
項目 | Send トレイト | Sync トレイト |
---|---|---|
目的 | オブジェクトの所有権を別のスレッドへ移動できる | オブジェクトへの不変参照を複数のスレッドから共有できる |
データの変更 | 許可されない | 許可されない |
スレッド間のデータ共有 | 所有権の移動のみ | 不変参照の共有のみ |
- すべてのプリミティブ型は
Send
かつSync
である。 - すべてのフィールドが
Send
かつSync
の構造体は、自動的にSend
かつSync
の構造体となる。