はじめに
Rustで値を交換する方法を調べたのでまとめました。
2つの変数の値を交換
以下の様に変数x
と変数y
があるとします。
2つの変数の値をどのように交換すればよいでしょうか。
let mut x = String::from("ABC");
let mut y = String::from("DEF");
すぐ思いつくのが一時変数を使用して交換する方法です。
let tmp = x;
x = y;
y = tmp;
Rustでは変数の値を別の変数に代入すると所有権の移動が発生します。
すると元の変数は未初期化状態になり使えなくなりますが、値を再代入することで使用可能になります。
※プリミティブ型は自動的に値がコピーされるため所有権の移動は発生しません
また、タプルを使用した方法だと1行で交換できます。
この方法だと変数x
と変数y
が一時的に未初期化状態になり値が再代入されます。
(x, y) = (y, x);
変数を未初期化状態にせずに値を交換するにはstd::mem::swap
関数を使います。
引数には変数の可変参照を渡します。
std::mem::swap(&mut x, &mut y);
Option::Someの中身と変数の値を交換
次に片方の変数がOption型の場合を考えます。
let mut x = String::from("ABC");
let mut y = Some(String::from("DEF"));
一時変数とタプルを使った交換方法は以下の通りです。
Option型の中身を取り出すにはunwrap
関数を呼び出します。
一時変数
let tmp = Some(x);
x = y.unwrap();
y = tmp;
タプル
(x, y) = (y.unwrap(), Some(x));
次にstd::mem::swap
関数を使った交換を考えます。
std::mem::swap(&mut x, &mut y.unwrap());
しかし上記のコードは正しく動作しません。
unwrap
関数を呼び出すと所有権の移動が発生するからです。
上記のコードの場合、変数x
には文字列DEF
が移動しますが
変数y
は未初期化状態になっています。
所有権を移動させずに中身の可変参照を取得するにはas_mut
関数を呼び出します。
as_mut
関数はOption<T>
からOption<&mut T>
の変数を生成して返します。
不変参照の場合はas_ref
関数を呼び出します。
よって正しく動作するコードは以下の通りです。
std::mem::swap(&mut x, y.as_mut().unwrap());
配列内の要素を交換
次に配列やベクタ内の要素を交換する方法を考えます。
let mut ary = [String::from("ABC"), String::from("DEF")];
この場合は一時変数やタプルを使用した交換はできません。
配列やベクタの要素は未初期化状態になるのが禁止されているからです。
一時変数
let tmp = ary[0]; // ary[0]が未初期化状態になるのでコンパイルエラー
ary[0] = ary[1];
ary[1] = tmp;
タプル
(ary[0], ary[1]) = (ary[1], ary[0]); // ary[0]とary[1]が未初期化状態になるのでコンパイルエラー
std::mem::swap
関数を使った交換もできません。
特定の変数に対する可変参照は複数存在できないからです。
std::mem::swap(&mut ary[0], &mut ary[1]); // 変数aryに対する可変参照が2つあるのでコンパイルエラー
配列やベクタには専用のswap
関数が存在します。
交換したい要素のインデックスを指定します。
ary.swap(0, 1);
まとめ
- 2つの変数の値を交換するには以下の方法がある
- 一時変数を使用する
- タプルを使用する
-
std::mem::swap
関数を使用する
- 配列やベクタの要素は専用の
swap
関数でしか交換できない
参考文献