nikkie-ftnextの日記

イベントレポートや読書メモを発信

Rust素振りの記 | 少し進んだ所有権まわりの理解をもとに、日数カウントダウンプログラムを書き直す

はじめに

思い出なんかいらん1 nikkieです。

Rustの素振りエントリ、扱うのは関数呼び出しと所有権です。

目次

所有権まわりの理解が少し進んだ

TechRAMENで聞いたt-wadaさんのLTがきっかけです2

Rustでは値が所有者の間を移動します。

  • 変数への代入
  • 関数の引数に渡す
  • 関数の返り値

元の所有者は未初期化状態となります。

あと何日か数えるRustプログラム

このことを知って理解が進んだ状態で、過去に書いたRustプログラム3を書き直してみたいと思ったのです。
ある日から別の日まで何日あるかを数えるプログラムです。

例:2023/06/17 から 2023/06/28 までは 11日

このプログラムは関数を使っていません。
今の私は、関数に実引数として渡すときに所有権の移動が起こることを知っています。
そこで参照を使って関数を書くと、所有権が移動せず、そのため使いやすい関数なのではないかと考えました

参照を使った実装

fn count_days(date1: &NaiveDate, date2: &NaiveDate) -> i64 {
    let duration = date1.signed_duration_since(*date2);
    duration.num_days()
}

NaiveDateの参照2つを受け取り、日数を表す整数を返す関数です。

  • &が参照
    • date1date2に渡した値の所有者は変わらない
  • *が参照解決4

main()関数を以下のように書き換え、

fn main() {
    let now: NaiveDate = Local::now().date_naive();
    let kojo_concert = NaiveDate::from_ymd_opt(now.year(), 8, 8).unwrap();
    println!(
        "『かがみの孤城』のコンサートの {} まで、あと{}日です",
        kojo_concert.format("%m/%d"),
        count_days(&kojo_concert, &now)
    );
    println!("Concert {} - Today {}", kojo_concert, now);
}

count_days()関数呼び出しで所有権が移動していないことを確認して、ホクホク顔でした。

% git stash
『かがみの孤城』のコンサートの 08/08 まで、あと5日です
Concert 2024-08-08 - Today 2024-08-03

書き換えなくとも所有権は移動していない - Copy型

参照を使わずに関数にしたらどうなるかを試していたところ、気づきがありました。

なんと参照を使わずに関数を抽出しても動くのです。
関数の呼び出しで所有権は移動していないのです

fn count_days(date1: NaiveDate, date2: NaiveDate) -> i64 {
    let duration = date1.signed_duration_since(date2);
    duration.num_days()
}

プログラム全体

なぜ参照を使わなくても所有権が移動しないのか。
思い出したのは『プログラミングRust 第2版』の「4.3 コピー型:移動の例外」でした

Copy型の値を代入すると、値は移動されず、コピーされる。

NaiveDateのドキュメントを見ていくと、コピー型であることが確認できます。
https://docs.rs/chrono/latest/chrono/naive/struct.NaiveDate.html#impl-Copy-for-NaiveDate
impl Copy for NaiveDate

所有権の移動の理解が進んだので「所有権が移動しない関数を導入できるのでは」と考えて手を動かしましたが、chronoライブラリの方でNaiveDateをコピー型(所有権の移動を考えなくてよい型)として実装済みだったということですね。

終わりに

過去に書いた日数計算のRustプログラムに、参照を使った関数を書くというアイデアを素振りしました。

  • 参照(&)を受け取る関数では所有権は移動しない
    • *で参照解決
  • NaiveDateCopy型なので、元から関数呼び出しで所有権は移動していなかった

『プログラミングRust』を読んで知識が増えたことで、自作した関数で所有権の移動は望んでいないので参照を使うという考え方ができるようになりました。
ライブラリの作者が気を回してくれて5Copy型(所有権が移動しない)として実装しているケースもあると体験しました

今回のコードの全体はこちらです。
date1date2の大小関係の縛りをなくしたり、テストコードを書いたりしています。


  1. ハイキュー!! vs稲荷崎より。私は思い出必要です(ミリオンライブとか知ってしまったので)
  2. 『プログラミングRust 第2版』5.2.1
  3. 当人がライブラリの中で使う上でも、所有権が移動しないほうが都合がいいのかもと思っています