[解決済み] Box、ref、&、*の理解と関連性
質問
Rustでポインタがどのように動作するのか、少し混乱しています。そこには
ref
,
Box
,
&
,
*
, といった感じで、連携しているのかよくわかりません。
現在、私が理解している方法は以下の通りです。
-
Box
は実際にはポインタではありません。ヒープ上でデータを割り当て、関数の引数でサイズのない型(特に traits)を渡すための方法です。 -
ref
はパターンマッチで、マッチするものを奪うのではなく、借用するために使われます。例えばlet thing: Option<i32> = Some(4); match thing { None => println!("none!"), Some(ref x) => println!("{}", x), // x is a borrowed thing } println!("{}", x + 1); // wouldn't work without the ref since the block would have taken ownership of the data
-
&
はボロー(借用ポインタ)を作るために使われます。もし私が関数fn foo(&self)
とすると、関数が終了した後に失効する自分自身への参照を取り、呼び出し元のデータだけを残すことになります。また、所有権を保持したいデータを渡すにはbar(&mydata)
. -
*
は、生のポインタを作るために使われます:例えばlet y: i32 = 4; let x = &y as *const i32
. C/C++のポインタについては理解していますが、これがRustの型システムでどのように動作し、どのように安全に使用できるのかがわかりません。また、このタイプのポインタのユースケースがどのようなものであるかもよくわかりません。さらに*
シンボルは参照解除に使用することができます(どんなものを、なぜ?)
どなたか、4 番目のタイプのポインターを説明していただき、他のタイプについての私の理解が正しいかどうか検証していただけないでしょうか。また、私が言及していない一般的な使用事例を指摘してくださる方を感謝します。
どのように解決するのですか?
まず、あなたが挙げた項目は、ポインタに関連するものであっても、実際にはすべて別物です。
Box
はライブラリで定義されたスマートポインタの型です。
ref
はパターンマッチのための構文です。
&
は参照演算子で、参照型では紋章を兼ねる。
*
は非参照演算子で、生のポインタ型では紋章を兼ねています。より詳しい説明は以下を参照。
Rustには4つの基本的なポインタ型があり、参照と生のポインタの2つのグループに分けることができます。
&T - immutable (shared) reference
&mut T - mutable (exclusive) reference
*const T - immutable raw pointer
*mut T - mutable raw pointer
最後の2つの違いは非常に薄く、どちらも何の制限もなく別のものにキャストすることができるので
const
/
mut
の区別は、ほとんどリントとして機能します。生のポインターは何にでも自由に作成でき、また、たとえば整数から何もないところから作成することもできます。
参照型とその相互作用は、Rustの重要な特徴の1つである「借用」を定義しています。参照は、いつどのように作成され、どのように使用され、どのように相互作用するかについて多くの制約があります。その代わり、参照は
unsafe
ブロックなしで使用できます。借用が正確には何であり、どのように機能するかは、この回答の範囲外ですが。
参照と生のポインタの両方が
&
演算子で作成できます。
let x: u32 = 12;
let ref1: &u32 = &x;
let raw1: *const u32 = &x;
let ref2: &mut u32 = &mut x;
let raw2: *mut u32 = &mut x;
参照と生のポインタの両方は、以下のようにして参照解除することができます。
*
演算子を使うことができます。ただし、ローポインタの場合は
unsafe
ブロックが必要です。
*ref1; *ref2;
unsafe { *raw1; *raw2; }
参照解除演算子が省略されることが多いのは、別の演算子である "ドット" 演算子(つまり。
.
など) は自動的に左引数を参照または非参照にします。ですから、例えば、次のような定義があるとします。
struct X { n: u32 };
impl X {
fn method(&self) -> u32 { self.n }
}
とすると、それにもかかわらず
method()
が取る
self
を参照する。
self.n
は自動的にそれを参照解除するので、わざわざ
(*self).n
. 同じようなことが
method()
が呼ばれた場合も同様です。
let x = X { n: 12 };
let n = x.method();
ここで、コンパイラは自動的に
x
で
x.method()
と書く必要はありません。
(&x).method()
.
次の最後のコードも、特殊な
&self
の構文も示しています。これは、単に
self: &Self
を意味し、もっと具体的に言うと
self: &X
をこの例では
&mut self
,
*const self
,
*mut self
も動作します。
つまり、Rustでは参照が主なポインタの種類であり、ほぼ常に使用されるべきものです。参照の制約を受けない生のポインタは、高レベルの抽象化(コレクション、スマートポインタなど)を実装する低レベルのコードや、FFI(Cライブラリとのやり取り)で使用されるべきものです。
Rustはまた 動的サイズ(またはサイズなし)型 . これらの型は静的に知られた明確なサイズを持たないため、ポインタや参照を通じてのみ使用することができます。しかし、ポインタだけでは十分ではありません。例えば、スライスの長さや形質オブジェクトのための仮想メソッドテーブルへのポインタなど、追加の情報が必要なのです。この情報は、サイズのない型へのポインタに "embedded" されるため、これらのポインタは "fat" となります。
ファットポインターは基本的に、データのピースへの実際のポインターといくつかの追加情報(スライスの長さ、形質オブジェクトの vtable へのポインター)を含む構造体です。ここで重要なのは、Rust はポインタの内容に関するこれらの詳細をユーザに対して完全に透過的に処理することです。
&[u32]
または
*mut SomeTrait
のような値で囲むと、対応する内部情報が自動的に渡されます。
Box<T>
はRust標準ライブラリのスマートポインタの1つです。対応する型の値を格納するのに十分なメモリをヒープ上に確保する方法を提供し、そのメモリへのポインタであるハンドルとして機能します。
Box<T>
は、それが指すデータを所有します。それが削除されると、ヒープ上の対応するメモリの一部が割り当て解除されます。
ボックスを考えるのに非常に便利な方法は、固定サイズの通常の値として考えることです。つまり
Box<T>
というのは、単に
T
と同じですが、マシンのポインタサイズに対応するバイト数を常に取る点が異なります。私たちは、(所有) のボックスが
値セマンティクス
. 内部的には、他の高レベルの抽象化と同様に、生のポインターを使用して実装されています。
Box
のような他のスマートポインタのほとんど全てに当てはまります)。
Rc
のように) も借りることができます。
&T
から
Box<T>
. これは、自動的に
.
演算子で自動的に行われますし、一度参照を解除して再度参照することで明示的に行うこともできます。
let x: Box<u32> = Box::new(12);
let y: &u32 = &*x;
この点については
Box
は組み込みポインタと似ていて、その中身に到達するために参照解除演算子を使うことができます。これは、Rust の参照解除演算子がオーバーロード可能であり、スマートポインタの型のほとんど (すべてではないにしても) に対してオーバーロードされているためです。これにより、これらのポインタの内容を簡単に借用することができます。
そして、最後に
ref
は、値の代わりに参照型の変数を取得するためのパターンの構文にすぎません。例えば
let x: u32 = 12;
let y = x; // y: u32, a copy of x
let ref z = x; // z: &u32, points to x
let ref mut zz = x; // zz: &mut u32, points to x
上記の例は参照演算子で書き換えることができますが、参照演算子は
let z = &x;
let zz = &mut x;
(これはよりイディオム的でもある)場合、以下のような場合があります。
ref
が必要な場合もあります。例えば、enum の variant を参照する場合などです。
let x: Option<Vec<u32>> = ...;
match x {
Some(ref v) => ...
None => ...
}
上記の例では
x
は全体の中で借用されているだけで
match
文の中だけで借りられるので
x
の後に
match
. というように書けば
match x {
Some(v) => ...
None => ...
}
では
x
は、この
match
によって消費され、それ以降は使用できなくなります。
関連
-
[解決済み] C - エラーは "free(): invalid next size (normal)" です。
-
[解決済み] static_cast, dynamic_cast, const_cast, reinterpret_cast はいつ使うべきですか?
-
[解決済み] スマートポインターとは何ですか?
-
[解決済み] const int*、const int * const、int const *の違いは何ですか?
-
[解決済み] Rust の `String` と `str` の違いは何ですか?
-
[解決済み】XがYを実装していない(...メソッドがポインタのレシーバを持つ)
-
[解決済み] Goで*int64をリテラルにするにはどうしたらいいですか?
-
[解決済み] ファットポインターとは何ですか?
-
[解決済み] 値の代わりに参照する範囲
-
[解決済み] Go HTTP ハンドラで、ResponseWriter が値で、Request がポインタであるのはなぜですか?
最新
-
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 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Luaにポインタのようなものはありますか?
-
[解決済み] C - エラーは "free(): invalid next size (normal)" です。
-
[解決済み] パラメータと戻り値におけるポインタと値の比較
-
[解決済み】XがYを実装していない(...メソッドがポインタのレシーバを持つ)
-
[解決済み] 「<type>はインターフェースへのポインターであり、インターフェースではない」混乱
-
[解決済み] Goで*int64をリテラルにするにはどうしたらいいですか?
-
[解決済み] ファットポインターとは何ですか?
-
[解決済み] 値の代わりに参照する範囲
-
[解決済み] Go HTTP ハンドラで、ResponseWriter が値で、Request がポインタであるのはなぜですか?