[解決済み] ファットポインターとは何ですか?
質問
ファットポインタという言葉をいくつかの文脈で読みましたが、具体的にどういう意味で、Rustではどのようなときに使われるのかがわかりません。このポインタは通常のポインタの2倍の大きさがあるようですが、その理由はよくわかりません。また、traitオブジェクトと関係があるようです。
どのように解決するのか?
ファットポインター(fat pointer")という用語は、以下のような参照や生のポインターを指すのに使われます。 動的にサイズ調整された型 (DSTs) - スライスや特性オブジェクトへの参照や生ポインタを指します。 ファットポインターはポインターに加え、DSTを"complete"にするいくつかの情報(例えば長さ)を含んでいます。
Rustで最もよく使われる型は
ではなく
DSTではありませんが、コンパイル時に既知の固定サイズを持っています。これらの型は
を実装しています。
Sized
特性
. 動的なサイズのヒープバッファを管理する型(例えば
Vec<T>
のように) は
Sized
の正確なバイト数をコンパイラが知っているからです。
Vec<T>
のインスタンスがスタック上に占める正確なバイト数をコンパイラが知っているからです。現在、Rustには4種類のDSTがあります。
スライス(
[T]
と
str
)
タイプ
[T]
(すべての
T
は動的にサイズ調整されます(特殊なquot;文字列スライスの
str
). そのため、通常、これは
&[T]
または
&mut [T]
というように、参照の後ろについています。この参照はいわゆるファットポインタと呼ばれるものです。確認してみましょう。
dbg!(size_of::<&u32>());
dbg!(size_of::<&[u32; 2]>());
dbg!(size_of::<&[u32]>());
これは(多少の後始末をしながら)印刷されます。
size_of::<&u32>() = 8
size_of::<&[u32; 2]>() = 8
size_of::<&[u32]>() = 16
というように、通常の型への参照が
u32
への参照は 8 バイトの大きさであり、配列
[u32; 2]
. この2つの型はDSTではありません。しかし
[u32]
はDSTであるため、その参照は2倍となります。
スライスの場合、DST を "completes"する追加データは、単に長さだけです。
ということは
&[u32]
はこのようなものです。
struct SliceRef {
ptr: *const u32,
len: usize,
}
トライット・オブジェクト (
dyn Trait
)
traitをtraitオブジェクトとして使用する場合(つまり、型消去、動的ディスパッチ)、これらのtraitオブジェクトはDSTとなります。例
trait Animal {
fn speak(&self);
}
struct Cat;
impl Animal for Cat {
fn speak(&self) {
println!("meow");
}
}
dbg!(size_of::<&Cat>());
dbg!(size_of::<&dyn Animal>());
これは(多少の後始末をしながら)印刷されます。
size_of::<&Cat>() = 8
size_of::<&dyn Animal>() = 16
もう一度
&Cat
が 8 バイトしかないのは
Cat
は通常の型だからです。しかし
dyn Animal
は形質オブジェクトであるため、動的にサイズが変更されます。そのため
&dyn Animal
は16バイトの大きさです。
traitオブジェクトの場合、DSTを完成させる追加データはvtableへのポインタ(vptr)です。 vtableとvptrの概念はここでは説明しきれませんが、この仮想ディスパッチコンテキストで正しいメソッド実装を呼び出すために使用されます。vtableは静的なデータで、基本的には各メソッドの関数ポインタのみが格納されています。これによって、traitオブジェクトへの参照は基本的に次のように表現されます。
struct TraitObjectRef {
data_ptr: *const (),
vptr: *const (),
}
(これは、抽象クラスのvptrがオブジェクト内に格納されるC++とは異なります。どちらのアプローチにも利点と欠点があります)。
カスタムDST
最後のフィールドが DST である構造体を持つことで、独自の DST を作成することは可能です。しかし、これはかなりまれなことです。顕著な例としては
std::path::Path
.
カスタムDSTへの参照やポインタもファットポインタとなります。追加されるデータは構造体内のDSTの種類に依存します。
例外です。外部型
で
RFC 1861
では
extern type
機能が導入されました。Extern型もDSTですが、それらへのポインタは
ではなく
ファットポインタです。もっと正確に言うと、RFCが言っているように
Rustでは、DSTへのポインタは指されているオブジェクトに関するメタデータを持ちます。文字列やスライスの場合はバッファの長さ、trait オブジェクトの場合はオブジェクトの vtable です。extern 型の場合、メタデータは単に
()
. これは、外部型へのポインタのサイズがusize
(つまり、ファットポインタではありません)。
しかし、もしあなたがCのインターフェースと対話しないのであれば、おそらくこれらのextern型を扱う必要はないでしょう。
上記では、不変参照に対するサイズを見てきました。ファットポインターは、ミュータブル参照、イミュータブル生ポインター、ミュータブル生ポインターのいずれでも同じように機能します。
size_of::<&[u32]>() = 16
size_of::<&mut [u32]>() = 16
size_of::<*const [u32]>() = 16
size_of::<*mut [u32]>() = 16
関連
-
[解決済み] 型付けされた関数ポインタ?
-
[解決済み] Luaにポインタのようなものはありますか?
-
[解決済み] スマートポインターとは何ですか?
-
[解決済み] なぜ、オブジェクトそのものではなく、ポインタを使用しなければならないのですか?
-
[解決済み] const int*、const int * const、int const *の違いは何ですか?
-
[解決済み] ポインタの「デリファレンス」とはどういう意味ですか?
-
[解決済み] パラメータと戻り値におけるポインタと値の比較
-
[解決済み】XがYを実装していない(...メソッドがポインタのレシーバを持つ)
-
[解決済み] 「<type>はインターフェースへのポインターであり、インターフェースではない」混乱
-
[解決済み] ファットポインターとは何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン