RustでPrashant N Mhatreさんによる新しいプログラミング言語を学ぶ15のエクササイズに挑戦しています。前回は再宣言についてこんな記事を書きました。その続きです。
課題
swapping two variables,
解答
コメントで、std::ptr::swap関数を使うと良い、という情報をいただいたので、今回はこの関数を読んでみます。
まずはstd::ptr::swap関数について、rust公式ドキュメントから引用します。
swap.rspub fn swap<T>(x: &mut T, y: &mut T) { unsafe { // Give ourselves some scratch space to work with let mut t: T = uninitialized(); // Perform the swap, `&mut` pointers never alias ptr::copy_nonoverlapping(&*x, &mut t, 1); ptr::copy_nonoverlapping(&*y, x, 1); ptr::copy_nonoverlapping(&t, y, 1); // y and t now point to the same thing, but we need to completely // forget `t` because we do not want to run the destructor for `T` // on its value, which is still owned somewhere outside this function. forget(t); } }
具体的に読んでいきましょう!
Rustを始めたばかりの私としてはこのコードの内容はざっくりとしかわかりません!
ですので詳細に読んでいきます。上から順に気になった文法をピックアップしていきます。
pub
publicの略……なのかな? エクスポートするときに使います。
T
Tはジェネリクスです。要するに任意の型みたいな意味らしい。
unsafe
コンパイラに対して、unsafe内の制約を少し緩めるように指示するものらしいです。Rustでは、危険な関数にはunsafeと書いてあり、unsafeな関数を実行するためには、unsafeを宣言したブロック内でないといけないようです。つまり、今回写しているコードは、unsafeなしだとコンパイルを通りません。具体的には、uninitializedとptr::copy_nonoverlapping()がunsafeです。
uninitialized
変数を定義したときに初期化しない。unsafe。ドキュメントをみると色々危ないよと書いてある。安全に初期化する方法は、後述のptr::copy_nonoverlapping()の他には、ptr::writeとptr::copyだけらしい。
ptr::copy_nonoverlapping
名前の通りオーバーラップせずにメモリの内容をコピーする関数です。unsafe。何がオーバーラップ(重なる)するのかといえば、コピー元メモリとコピー先メモリです。比較対象のptr::copy関数は、コピー元とコピー先のメモリ領域が重なる可能性があるが、ptr::copy_nonoverlapping関数だとメモリ領域は重ならない(あるいは重ねて指定することができない)となります。
mem::forget
こちらはsafety。指定した変数のメモリ領域そのものに変更を加えずにメモリ領域と変数の関連性だけをなくします。forget関数の段階でtとyは同じメモリ領域を参照しているため、tに何らかの変更を加えるとyにも影響が出ます。スコープを抜ける段階でtに変更が加えられる(削除とか)影響をyに与えないようにするための関数です。
まとめ
swapするの結構難しいですね。メモリまで考えたプログラミングは今まであまりやってこなかったので、学びが得られたのは新しい言語(特にC,C++,Rustみたいなの)に触っているからこそです。
次回は、最大値と最小値を求めます。