RustのSizedトレイトと動的サイズ型を深く掘り下げる
Lukas Schneider
DevOps Engineer · Leapcell

はじめに
Rustは、安全性とパフォーマンスで知られるシステムプログラミング言語です。Rustでは、コンパイル時に型のサイズを決定することが非常に重要です。ただし、一部の型はコンパイル時にサイズが不明なため、Rustでは動的サイズ型(DST)という概念が導入されています。型のサイズがコンパイル時にわかるようにするために、RustはSized
トレイトを導入しています。
この記事では、RustのSized
トレイトについて、その定義、目的、使用法、および動的サイズ型との関係について詳しく掘り下げます。
Sized
トレイトとは?
Rustでは、Sized
は、型のサイズがコンパイル時にわかっているかどうかを示す特別なトレイトです。Sized
トレイトは次のように定義されます。
pub trait Sized { // このトレイトにはメソッドはありません。型が既知のサイズを持つことを示すために使用されます }
すべての型は、特別な構文を使用して動的にサイズが決定されると明示的にマークされない限り、デフォルトでSized
であることに注意することが重要です。
動的サイズ型とSized
トレイトの関係
Rustでは、動的サイズ型(DST)は、サイズをコンパイル時に決定できず、実行時に知る必要のある特殊な型です。DSTの主な例は、参照型とトレイトオブジェクトです。Sized
トレイトは、型がコンパイル時に既知のサイズを持つかどうかを示す指標として機能します。
参照型とSized
トレイト
参照型は、Rustの動的サイズ型の1つです。Rustでは、参照は&
を使用して作成され、別の型の値を参照します。参照型自体のサイズは、参照されている値のサイズに基づきません。
fn main() { let x = 42; let reference = &x; // xへの参照 }
上記の例では、変数x
を作成し、&
を使用してx
の値への参照reference
を作成します。参照自体のサイズは、参照される値のサイズに依存しません。
ただし、参照型(&T
)のサイズは常に固定(ポインタのサイズ)であるため、参照型はDSTとは見なされません。これは、参照される値が別の場所(通常はスタックまたはヒープ)に存在し、参照のみが保存されるためです。
トレイトオブジェクトとSized
トレイト
トレイトオブジェクトは、Rustの別の種類の動的サイズ型です。トレイトオブジェクトを使用すると、異なる具象型の値を共通のインターフェイス(トレイト)を通じて処理できます。トレイトオブジェクトのサイズは、参照される特定の型によって異なるため、コンパイル時にはわかりません。
trait Shape { fn area(&self) -> f64; } struct Circle { radius: f64, } impl Shape for Circle { fn area(&self) -> f64 { self.radius * self.radius * std::f64::consts::PI } } fn main() { let circle: Circle = Circle { radius: 5.0 }; let shape: &dyn Shape = &circle; // トレイトオブジェクトを介した具象型への参照 }
上記の例では、トレイトShape
を定義し、Circle
構造体に対してそれを実装します。次に、トレイトオブジェクト&dyn Shape
を作成して、具象型Circle
の値を参照します。実際のサイズは具象型によって異なるため、トレイトオブジェクトのサイズはコンパイル時にはわかりません。
トレイトオブジェクトには、具象値への隠しポインタが含まれており、それを使用してメソッドを呼び出します。その結果、&dyn Trait
のサイズは常にポインタのサイズになります。
Sized
トレイトの制限事項
Rustでは、動的サイズ型(DST)には、特にジェネリクスおよびトレイトの実装で使用する場合に、いくつかの制限があります。
ジェネリクスの制限
DSTでジェネリクスを使用する場合、?Sized
構文を使用して、サイズが決定されていない型を明示的に許可する必要があります。
// 間違い:DSTをジェネリックパラメータとして使用することはできません fn process_data<T>(data: &[T]) { // データを処理する } fn main() { let vec_data = vec![1, 2, 3, 4, 5]; process_data(&vec_data); // コンパイルエラー:DSTをジェネリックパラメータとして使用することはできません }
上記の誤った例では、DST [T]
をジェネリック関数のパラメータとして使用しようとしましたが、これはデフォルトでは許可されていません。DSTを許可するには、ジェネリックパラメータを?Sized
でマークする必要があります。
// 正しい:DSTをジェネリックパラメータとして許可する fn process_data<T: ?Sized>(data: &[T]) { // データを処理する } fn main() { let vec_data = vec![1, 2, 3, 4, 5]; process_data(&vec_data); // 正しい:DSTをジェネリックパラメータとして使用 }
この正しい例では、型パラメータT
で?Sized
を使用すると、動的サイズ型にすることができます。
トレイト実装の制限
安全上の理由から、RustでDSTのトレイトを実装する場合は、?Sized
構文を使用して、サイズが決定されていない型を許可する必要があります。これは、トレイトオブジェクトの場合、コンパイラはコンパイル時ではなく、実行時に具象型のサイズを決定する必要があるためです。
trait Shape { fn area(&self) -> f64; } struct Circle { radius: f64, } impl Shape for Circle { fn area(&self) -> f64 { self.radius * self.radius * std::f64::consts::PI } } // 間違い:`?Sized`なしでDSTのトレイトを実装することはできません impl Shape for dyn Shape { fn area(&self) -> f64 { // トレイトメソッドを実装する } } fn main() { let circle: Circle = Circle { radius: 5.0 }; let shape: &dyn Shape = &circle; shape.area(); }
誤った例では、Shape
トレイトをdyn Shape
に直接実装しようとしましたが、これは許可されていません。
// 正しい:`?Sized`を使用してDSTのトレイトを実装する impl Shape for dyn Shape + ?Sized { fn area(&self) -> f64 { // トレイトメソッドを実装する } } fn main() { let circle: Circle = Circle { radius: 5.0 }; let shape: &dyn Shape = &circle; shape.area(); }
正しい例では、?Sized
を使用して、dyn Shape
を動的サイズにできることを示し、それに対するトレイトの実装を可能にします。
使用法
型がSized
トレイトを実装しているかどうかを確認する
Rustでは、std::mem::size_of
のような関数を使用すると、型がSized
であるかどうかを推測できます。
fn main() { println!("i32 is Sized: {}", std::mem::size_of::<i32>() == std::mem::size_of::<i32>()); println!("&i32 is Sized: {}", std::mem::size_of::<&i32>() == std::mem::size_of::<usize>()); }
上記の例では、size_of
を使用して型サイズを比較します。i32
はSized
型であるため、出力はtrue
です。参照型&i32
もSized
です。これは、参照のサイズは常にポインタのサイズ(通常はusize
と同じ)であるためです。
Sized
トレイトでジェネリクスを制約する
ジェネリックパラメータを明示的に制約して、Sized
型のみを受け入れるようにすることができます。
fn process_data<T: Sized>(data: &[T]) { // データを処理する } fn main() { let vec_data = vec![1, 2, 3, 4, 5]; process_data(&vec_data); // 有効:`Sized`型のみがジェネリックパラメータとして許可されます }
上記の例では、process_data
関数は、ジェネリックパラメータT
としてSized
トレイトを実装する型のみを受け入れます。
?Sized
を使用して動的サイズ型をサポートする
トレイトでDSTをサポートする場合は、?Sized
でマークできます。
trait Shape { fn area(&self) -> f64; } struct Circle { radius: f64, } impl Shape for Circle { fn area(&self) -> f64 { self.radius * self.radius * std::f64::consts::PI } } impl Shape for dyn Shape + ?Sized { fn area(&self) -> f64 { // トレイトメソッドを実装する } } fn main() { let circle: Circle = Circle { radius: 5.0 }; let shape: &dyn Shape = &circle; shape.area(); }
上記の例では、?Sized
を使用してdyn Shape
をDSTにできることを示しました。これにより、トレイトを実装できます。
動的サイズ型とSized
トレイトの比較
動的サイズ型(DST)とSized
トレイトはどちらも型サイズの概念に関連していますが、意味と目的が異なります。
動的サイズ型は、サイズをコンパイル時に決定できず、代わりに実際のデータに基づいて実行時に解決する必要がある特殊な種類の型です。DSTには、主に参照型とトレイトオブジェクトが含まれます。DSTを使用する場合、それらの制限事項(直接インスタンス化できないことや、ジェネリクスでの使用が制限されていることなど)に注意することが重要です。
一方、Sized
トレイトは、型のサイズがコンパイル時にわかっているかどうかを示すために使用される特別なトレイトです。Rustのすべての型は、明示的に指定されていない限り、デフォルトでSized
です。Sized
トレイトは、サイズ制約に関する型の安全性を確保するために、ジェネリクスおよびトレイトの実装で特に役立ちます。
結論
この記事では、RustのSized
トレイトについて、その定義、目的、使用法、および動的サイズ型との関係について、詳細な説明と分析を提供しました。
Sized
トレイトは、型サイズがコンパイル時にわかっていることを保証することにより、Rustで重要な役割を果たします。Sized
トレイトを理解し、適切に使用することで、より安全で効率的なコードを作成できます。
Rustプロジェクトをホストするための最適な選択肢であるLeapcellをご紹介します。
Leapcellは、Webホスティング、非同期タスク、およびRedis向けの次世代サーバーレスプラットフォームです。
多言語サポート
- Node.js、Python、Go、またはRustで開発します。
無料の無制限のプロジェクトをデプロイする
- 使用量に対してのみ支払い、リクエストや料金はかかりません。
比類のない費用対効果
- アイドル料金なしの従量課金制。
- 例:25ドルで平均応答時間60msで694万リクエストをサポートします。
合理化された開発者エクスペリエンス
- 簡単なセットアップのための直感的なUI。
- 完全に自動化されたCI/CDパイプラインとGitOps統合。
- 実用的な洞察を得るためのリアルタイムのメトリックとロギング。
簡単なスケーラビリティと高パフォーマンス
- 高い並行処理を簡単に処理するための自動スケーリング。
- 運用上のオーバーヘッドはゼロで、構築に集中できます。
ドキュメントで詳細をご覧ください!
Xでフォローしてください:@LeapcellHQ