rust: 文法エラーの時に .clone() を適当に試さないようになりたい
2025/10/29
Rust を vscode で書いていると、文法エラーになるときは事前に指摘してくれることが多い。
“borrow of moved value: hogehoge” というのもよく見るエラーだ。
「もしかしたら…」と期待して & を付けて成功するのはまだよい。
それでダメだったとき、もう何も考えず .clone() として成功してしまうとそのままにしてしまう。
これはよろしくなかろう。
.clone() を使う理由
ちょうどそういうエラーが出たので引用しよう。
error[E0382]: borrow of moved value: `signer`
--> src/2.rs:42:24
|
28 | let signer: PrivateKeySigner =
| ------ move occurs because `signer` has type `LocalSigner<ecdsa::signing::SigningKey<Secp256k1>>`, which does not implement the `Copy` trait
...
33 | .wallet(signer)
| ------ value moved here
...
42 | let from_address = signer.address();
| ^^^^^^ value borrowed here after move
|
help: consider cloning the value if the performance cost is acceptable
|
33 | .wallet(signer.clone())
| ++++++++
For more information about this error, try `rustc --explain E0382`.
こんな内容だ。
- L.28 で定義した
signerというCopyトレイトを実装してない値は借用されて移動してしまった - L.33 で移動している
- L.42 で移動された後に使われている
- L.33 で
.clone()することを考えてもいいんじゃないかな
借用されて他のところに所有権が移動してしまうと、それ以降はその変数を扱うことができない。
それなら移動しても困らないように .clone() で複製を作ってそちらが移動しても元の値は残る、という考え方だ。
まあ、特段悪いことは無いと思う。
ただまあ「コピーぃ?」と思わなくはない。
どういう塊でコピーされるかよくわからないので、なんとなく負荷が高そうな処理に感じる。
自分で実装した関数とかなら引数を見直すこともできるが、提供されているものだとそうもいかない。
参照を使っていないのも、何か理由があってのはずだ。
どうしようもない気がしてきた
それらしい記事があったのだが、自分が制御できない範囲についてはどうしようもない気がしてきた。
Rc や Arc というのがあるようだが、そもそもそれを使っても大丈夫かどうかは中身まで見ないとわからないし、
なによりまだ私が慣れていないので見極められない気がする。
一応 ChatGPT氏にも尋ねてみたが同じようなものだった(ChatGPT)。
- [Rc
は、参照カウント方式のスマートポインタ - The Rust Programming Language 日本語版](https://doc.rust-jp.rs/book-ja/ch15-04-rc.html)
今回エラーが出た箇所では、signer という変数を引数で与えた後に signer のメソッドを呼び出したのでそうなったが、
幸いメソッドの方は &self だったし、順序が逆でも問題なかったので入れ替えることで .clone() せずに済んだ。
いろいろ試す
意味が無いコードだが、borrow() が Rent を参照ではなくそのまま引数で受け取るので let b の行で a が move して次の let c の行では使えない。
fn main() {
let a = Rent{ v: 10 };
let b = borrow(a);
let c = borrow(a);
println!("{} -> {}", b, c);
}
struct Rent {
v: i32,
}
pub fn borrow(r: Rent) -> i32 {
r.v * 2
}
では Rc というものを使ってみよう。
といっても単に使っただけだと型が不一致でエラーになる。ここだと let a で既にダメだ。
fn main() {
let a : std::rc::Rc<Rent> = Rent{ v: 10 }.into();
let b = borrow(a);
let c = borrow(a);
println!("{} -> {}", b, c);
}
struct Rent {
v: i32,
}
pub fn borrow(r: Rent) -> i32 {
r.v * 2
}
ここまでやればできるが、これは呼び出し先が Rc を使うように改造しているので期待には添わない。
fn main() {
let a = std::rc::Rc::new(Rent{ v: 10 });
let a2 = std::rc::Rc::clone(&a);
let b = borrow(a2);
let c = borrow(a);
println!("{} -> {}", b, c);
}
struct Rent {
v: i32,
}
pub fn borrow(r: std::borrow::Cow<_, Rent>) -> i32 {
r.v * 2
}
沿わないついでに std::borrow::Cow を使ってみようとしたが、これは ToOwned の実装が無いとダメと言うことで止めた(面倒だから)。
おわりに
呼び出したい関数が「所有権をくれ」といっているので、渡すしかないのだ。