Perl日記

日々の知ったことのメモなどです。Perlは最近やってないです。

Rust 参照の分配(destructuring)のパターンのメモ

参照の分配

クロージャの引数のところ

https://doc.rust-jp.rs/book-ja/ch18-03-pattern-syntax.html?highlight=Point#%E5%8F%82%E7%85%A7%E3%82%92%E5%88%86%E9%85%8D%E3%81%99%E3%82%8B

VS Code が教えてくれる書き方の中に、クロージャの引数部分で、|&x| のように & がついていて、参照なのか何なのかよくわからないな、let の変数束縛時に左辺に & つけないし……、と思ってたら↑に書いてあった。

|&x| と書くと、x はむき出し状態として使うことができる。

下の例は、iter() も filter() も、要素が参照になるので、つなげると &&T が filter 内部に伝わっている。

let v: Vec<String> = vec![
    String::from("foo"),
    String::from("bar"),
    String::from("baz"),
    String::from("foo"),
];

// String と  &str は比較可能みたいです

// |&s| にしたら s は &String になる
let v2: Vec<&String> = v.iter().filter(|&s| s == "foo").collect();
// let v2: Vec<&String> = v.iter().filter(|&s| (*s).eq("foo")).collect();
println!("v2: {:?}", v2); // v2: ["foo", "foo"]

// |s| にしたら s は &&String になる
// 自動で一つデリファレンスされての比較では、「can't compare `&String` with `str`」エラーになる
// let v3: Vec<&String> = v.iter().filter(|s| s == "foo").collect();

// 手動で一つデリファレンスすると動く
let v3: Vec<&String> = v.iter().filter(|s| *s == "foo").collect();
println!("v3: {:?}", v3); // v3: ["foo", "foo"]

// もちろん手動で二つデリファレンスも OK
let v3: Vec<&String> = v.iter().filter(|s| **s == "foo").collect();

// |&&s| にすると、いきなりむき出しになるが、暗黙のコピーが発生するらしく、Vec<String> だとエラーになってしまった
let v4: Vec<&String> = v.iter().filter(|&&s| s == "foo").collect();
// error[E0507]: cannot move out of a shared reference
//  move occurs because `s` has type `String`, which does not implement the `Copy` trait

Copy トレイト実装済のプリミティブな型なら |&&s| でもいける。

let vv = vec![1, 2, 3];
let v5: Vec<_> = vv.iter().filter(|&&s| s == 2).collect(); // s は i32
println!("v5: {:?}", v5); // v5: [2]

Enum のデータのところ

if let とか、match とかで、Some(T) のデータ部分も &x みたいに & をつけてむき出しで使える。

let s = "foo";
let ss: Option<&&str> = Some(&s);
if let Some(&s1) = ss {
    println!("{}", s1); // s1 は &str
}
match ss {
    Some(&s2) => println!("{}", s2), // s2 は &str
    _ => (),
}

でもこれも Copy トレイトを実装している必要があるので、String だとエラーになった。

あと & って変数とか型とかだけではなく、値に直接つけることもできるというのをメモ。

let i = 123;
let o = Some(&i);
assert_eq!(o, Some(&123)); // これ!