NF

地方で働くプログラマ

最強寒波

流石に寒い。部屋の中も寒いし、気圧がアレなのかちょっと頭痛もする。
もう少しでピークは抜けるっぽいけど。
 
ーーー
GRANDFINALS(グランドファイナル) | ストリートファイターリーグ | CAPCOM
SFリーグ決勝、これはマジで面白かったので買って良かった。というか、半年間のリーグはタダで観てたので、まぁ最後くらいお布施を。軽く感想。
 
ネモさん率いる熊本とウメハラの居る名古屋はどっちも好きなチームだったので、どっちを応援するか迷ったけど、気持ち熊本寄りで。1巡目はまさかの熊本ホームで0-40からのスタート、最後に負けた側が対策できるので有利とか聞くけどそれにしても一方的な。最強Shutoが負けたのもデカい。2巡目で30-50まで戻して3巡目はShutoがふーどを倒して60-60で4巡目へ、一番熱い展開。4巡目の名古屋はめっちゃ迷ってたけど、結局ウメハラが出て負け、熊本勝利になりました。
決勝リーグのウメハラは期待してた程勝ち星は取れなかったけど、半分くらいの歳のShutoに「俺らやられたらやり返すんで」って言ったのめっちゃ良いなと思った。まぁ本節の成績は1位だったようだし、スト5最後の年はかなり活躍した感ある。ウル4の最後の年もそんな感じだった気がする。
 
決勝Good 8 Squad戦、大将Shutoにカワノがルークで出て勝利、1巡目は熊本10-30でビハインド。2巡目ホームでもポイントが取れず、20-60で1つも落とせない状態で3巡目。ひぐちは勝ったけどネモさんがぷげらを当てられて負け、優勝はGood 8 Squadに。
ネモさん、今期あまり活躍出来なかったけど、最後の試合はめっちゃいい勝負だったしアレで負けるならもう仕方ない感じする。去年はShuto不調の間ネモさんがチームを支えてた感じだったので、今年は逆になってるのは結構エモいな。ネモさんが最後の挨拶で涙流してるのも見てて来るものがありました。
 
来年からスト6だけど、どうなるかな…
 
ーーー


格ゲー繋がりで、なんか500回らしいので久々に見た。
確かかずのこ辞めてハイタニ入ったくらいまでは見てたけど、そもそも水曜の方をメインで観てたので本当に久々。でもクイズのコーナーとか幾つか覚えてるのもあって、懐かしかった。動画とかもっとあると良かったけど。水曜の方はもう560回とかみたいだけど、500回で似たような振り返りやってたなら見てみたい気はする。
 
ーーー
UL Systems Programming Contest 2023(AtCoder Beginner Contest 286) - AtCoder
なんか、100万円ABCとか話題になってたのでちょっと見てみた。学生1位が100万円で2位以下は賞金なしというストロングスタイル。社会人1位も10万らしい。
自分が見始めた時点(開始30分くらい)で、上位15人が7問解いててあと1問の状態。41:10に最初の全完がノーペナで出て、社会人1位が確定。続いて2番目に42:28で学生の全完が出たけど4ペナ、3番目に45:50でノーペナ全完が出て、この人が学生1位に。2分差くらいで4番目の人が全完して学生2位。その後も全完者が続き、結局2番目に全完した人は最終的に11位くらいでした。ABCだと、4ペナはかなり最終順位に影響するんですね。
 
学生1位と2位の人はICPCとかMetaのコンテストでも見た名前だった。あと2位の人、このポイントで何で冠付いてないんだと思ったら、最後のレーティングが2020年末だからアクティブ期間を過ぎてるのね。AGCが年6回?くらいだと色々厳しそう。
Scoreboard | Meta Hacker Cup - 2022 - Final Round
あと関係ないけど思いついた事を書いてるが、今のあっとこのランキングって上位10人に日本人いないんですね。11位がPFNの人で、19位が今回の学生1位の人。
 
ーーー
月曜、久々に声優ラジオを色々見れた。

特に声グララジオは時間的に全然見れてないので、動画に残すかリピート放送の時間を変えてほしい(リピートも同じような夕方で一般社会人は見れない)。ゲストの古川登志夫さんの、教える側で大事にしてるのは個性を引き出す事とか、自身の「役の幅を広げるために努力したという事はない」みたいな話はかなり面白かったです。あと千田是也さんという名前が出たけど、なんかちょっと調べただけで凄い人だな。
折角見れたので声グラにメールでも送ろうと思ったけど、筆不精(?)で結局送れてない。
 
ーーー
旅行
大体行きたいところを決めた。北九州とか山口で見たいところが大概車無いとアクセス厳しい(元乃隅神社とか)ので、ちょっと諦めた。あと所謂映え系のところは観光地!って感じの主張が強くて調べてる内にいいか…ってなって来る。本当は聖地・霧島神社とかも行きたかったけど、やっぱりアクセスが微妙でした。
取り合えず経県値的には今回ので47都道府県埋まるので、その後のことは別で考える。

「並行プログラミング入門」読みすすめ その5(5章のさわり)

「並行プログラミング入門」読みすすめ その1 - NF
「並行プログラミング入門」読みすすめ その2 - NF
「並行プログラミング入門」読みすすめ その3(入力とか) - NF
「並行プログラミング入門」読みすすめ その4(借用とか) - NF
5章の序盤だけ。

5.1 並行サーバ

初回の日記で並行サーバよく分からんみたいに書いてたけど、サンプル読んでて理解した。
 
1つ目の例のaccept()はコネクション要求しか受けれないけど、2つ目の例のepoll()はコネクション要求とデータ受信完了の2つを受けられるので、より細かい粒度で処理できるよ、ってだけっぽい。んで、細かい粒度で処理できない方を「反復サーバ」、できる方を「並行サーバ」と呼ぶという感じで理解した。
 
合ってるか余り自信ない。最初、なんで実装例はシングルスレッドなのに「並行サーバ」なんだって思ったし、受けるイベントが1つから2つになっただけで名前が違うのがしっくり来なかった。一応、今はメリットとかも理解したと思う。

5.2.1 コルーチン

何か、色々調べてると明確な定義が無い用語らしい。
ダイジェスト版「コルーチン」とは何だったのか - HackMD
日刊Coroutines(0) コルーチンのおさらいと近年の動向(若干) - lilyum ensemble
 
取り合えず、途中で止めたり再開できるスレッド、と理解した。
Rustはコルーチンの仕様が無いらしく、本のサンプルでは状態マシンを地力で実装してる。
あと「対称コルーチン」と「非対称コルーチン」があって、止まる時に別のスレッドを開始(再開)するのが前者、呼び出し側がいて止めたり再開したりを出来るのが後者?


ちょっと、もう少し調べる。

「並行プログラミング入門」読みすすめ その4(借用とか)

「並行プログラミング入門」読みすすめ その1 - NF
「並行プログラミング入門」読みすすめ その2 - NF
「並行プログラミング入門」読みすすめ その3(入力とか) - NF
4回目、まだ基礎。
「借用」が新鮮だったので掘り下げ。この本ではそこまで詳しく書いてない(特に「図2-3 借用時の状態」は分かり辛すぎるような…)ので、他のサイトさんとかで調べつつ。あと所有権周り、説明する人によって使う用語や解説ポイントが結構違うので、ちょっと混乱する。

基礎:Rustにおける変数の種類

  • mutable変数
  • immutable変数(または、単純に変数というらしい)
  • mutable参照
  • immutable参照(または、単純に参照というらしい)

上の2つは、C++で言う(普通の)変数・const変数だと思う。
mutable参照は(普通の)参照でいいのかな?(追記:後述の通り間違ってた)
immutable参照、これはC++とかでは見た事ない概念ぽい。
 

サンプルコード

4種類の変数を取り合えず宣言。

struct Foo {
    x:i32
}
fn main() {
    let mut a: Foo = Foo{x:10};  // mutable変数
    let     b: Foo = Foo{x:20};  // immutable変数
    let     c      = &mut a;     // mutable参照
    let     d:&Foo = c;          // immutable参照
    
    a.x = 11;  // mutable変数は書き込み可
    println!("{}", a.x); // 当然読み込みも可
    
    //b.x = 21;  // immutable変数は書き込み不可
    println!("{}", b.x); // 読み込みは可
}

ここまでは普通。
 

mutable参照について

    println!("{}", a.x);
    //println!("{}", c.x);  // コンパイルエラー

参照cの宣言後に参照先が使用されていると、参照の使用は読み書き共に不可!!
エラーは、参照cの初期化の行で出る。(cannot borrow `a.x` as immutable)。

    //println!("{}", a.x);
    c.x = 12;  // mutable参照は書き込み可
    println!("{}", c.x);

宣言後に参照先が使用されなければ、参照で読み書き可。
 

immutable参照について

    //d.x = 22;  // コンパイルエラー
    println!("{}", d.x);

参照の読み込みは出来るが、書き込みは出来ない。これはmutable参照と異なるところ。

    //println!("{}", a.x); // コンパイルエラー
    println!("{}", c.x);
    println!("{}", d.x);

一度宣言したら参照先の変数かどちらかかしか使用できない。これはmutable参照と同じ。
また、このケースだと参照先のmutable参照(c)は同時に読み込める。
但し、cに書き込む場合は、dは読み込みも不可になる。


という感じで、C++でイメージしてた参照とは違っていた。
ただ、本に載ってる「貸与中」がまだちゃんと理解出来て無いっぽくて、最後の例で参照cとdに同時にアクセスできるあたりしっくり来てない(cとdが同時に所有権を保持しているように見える。)。一応、破壊的代入を行えるのが同時に1つ、と言う事で読み込みはOKと言う事なんだろうけど、そうするとaはなんで読み込みできないのだろう、という疑問が。
色々読んでみて理解を深めるしかなさそうです。
 
あと、mutable参照とimmutable参照で、宣言時に後者だけ型指定するっていうのが凄く違和感なんですが、前者は省略されてるだけって事なんだろうか。型を指定してmutable参照を宣言したかったんだけど、色々試してもやり方分からず、宿題。

    let     c      = &mut a;     // mutable参照
    let     d:&Foo = c;          // immutable参照


ーーー
借用と全然関係ないですが、本読んでて「関数」と「メソッド」がRsutだと厳密に使い分けられてるってのが新鮮でした。C++だと、どっちも関数って呼ばれてる気がする?(TODO:調べる)

てきとう日記

だらだら書きます。
 
ーーー


ティラミス味あったので買ったけど、微妙だったな…バニラ買えば良かった
 
ーーー
お仕事、最近つまらないなと思う。
存在感は低くていいんだけど、なんかメイン所から遠ざけられてる感じもする。あと、周りで10分で出来そうな作業を1日掛けてやってるのを見て、口出したいみたいな気持ちも減ってきたな。まぁ春になったらちょっと変わりそうだし、それまでは低空飛行でいいか。
 
ーーー
東京旅行記をたまに読み返してる。
超絶方向音痴なので東京の位置関係が全く分からず過ごしてきたけど、最近イベントとかで色々行って少しづつ分かってきた。旅行記を読み直すと理解度が上がって良い。竹橋は神保町の近くだったのか…とかそういう低レベルな理解ですが。
人形町の方行ったことないな、って思って本に載ってるキラク行くか~と調べたら、なんかお家騒動で分裂したらしい。洋食屋でもそういうのあるんだ。
かわいそうな「そときち」、「そよいち」で再スタート、多難な道
ラクを繁盛させてた人達が今は「そよいち」という近くのお店に、創業者の親族?の人が「キラク」に残ったとか何とか。良く分かってない。んで今はキラクの方がコロナなんやらで休業してるぽい。あと口コミもキラクの方がかなり悪くて、行くなら「そよいち」かーとなってます。
 
ーーー
春の九州旅行どうするか迷ってたけど、4月からJALの先得廃止とかあるし、他もいつ上がるか分からんので、この機に行く事にした。ANAが来週からSUPER VALUE PREMIUMの予約始めるので、ツアーはそっち見てみるかね。
 
JAL、早割廃止報道に「全く我々の趣旨とは違う」
あと結構前の記事ですが。色々あるから値上げ自体は仕方ないとは思ってるけど、このJAL専務の説明はかなり印象悪いな。ちなみに、羽田→鹿児島の先得だと3/15(木)は6,970円、改定後の4/12(水)は13,790円。もうJAL使う事かなり減りそうだけど、中途半端なマイルをどうにかしたいな。
 
ーーー
技術的負債の返済から改善する開発者体験 - Techmee vol.5 - YouTube
思ったより長かったのであとで見る

初詣とか色々

上野の方に用事があったので、ついでに湯島天満宮靖国神社へ。

来週後半は春のような暖かさ 14日(土)は東京も17℃予想
雨は降ってたけど、前日温かかったせいで湯島天満宮は梅が咲いてた。
 
御茶ノ水付近の行きたい中華屋が臨時で閉まってた。最近こういう事ばっかだけど、公式+ツイッターで分からんものはもう仕方なく。あとこの日はさぼうるとかラドリオとか普段行く時に限って閉まってる店も開いてたけど、どっちも行列。特にさぼうるはキャリーケース持った外国人とかが多くて、もう観光地って感じやね。

いつも並んでる神田まつやがすぐ入れたので初めて入った。もりそば頼んだけど、コシがあって好きなタイプだった。卓上の山椒とかゆず七味も良い感じ。次は大盛+天ぷら付き食べに行きたい。あと、酒飲まないけど焼き鳥。
 

湯島の帰りにうさぎやCAFÉに寄ってみた。うさぎあん蜜、写真でわかりづらいけど割とボリュームある。おしゃれ系なのもあって値段は結構強気設定だけど、それなりに美味しい。狭山茶?はティーバッグ置き場を2つ置くことでうさぎの耳になってます、という説明があった。

 

帰りの秋葉原。アトレは見た事ないけど名前は知ってる俺ガイル。あとうたわれとか。
 
ーーー
あと別の日だけど、肉というかステーキ食べたい欲が高まって来たのでブロンコビリーに。
なんかすぐ行けそうなのがここくらいしか無かった。いきなりの店はそこらにあるけど、何か口に合わないので。休日昼とか言ったせいで激混み。混んでるとは思うけど油断してた。まぁゲームしてたら30分くらいで呼ばれた。

ランチの炭焼きやわらかランチステーキ160g、小さい?と思ったけどまぁこんなもん。ご飯は大盛無料。メニュー写真と違って肉が塊になってるのはともかく、中真っ赤でちょっと不安になった。まぁ1300円でこれなら、それなりに満足出来ました。
次は空いてそうな時間に行って、がんこハンバーグ食べるか。びっくりドンキーのが近所にあるけど、ハンバーグもブロンコの方が旨そうなんだよね。ただ、基本家族連れ想定なのか1,2人席が無いっぽい?なんか一人で入ってすいませんという気持ちに。
 
ーーー
ニュースとか備忘録
サービス終了「東方ダンマクカグラ」のクラファンに約2億円集まる 目標の1288% 買い切り版リリースへ
目標の1000%越えは凄いな。きらファンもこんな感じになれば。。
しかし、これだけ支持があってもサービス終了にはなってしまうものなのね。
 
なぜ国産旅客機「MRJ」は失敗したのか 現場技術者に非はなかった? 知られざる問題の本質とは
分かり易かった。どうすれば良かったんだろうなー
 
はじめての投資 - なるせにっき
ニュースではないけど、お金の話で分かり易かったので。
 
立民・泉代表「何だか息苦しい」…左派系の壁に直面、安全保障・改憲論議も進まず
内容自体には触れないけど、また内輪もめしてるな…という印象。
個人的に1強より2大政党とかあった方が良いと思ってるんですが、そうなる未来は遠そう。
 
ーーー
本とか。
森博嗣ジグβは神ですか」読了。
加部谷が社会人になってたり、いつものメンバーが揃ったり。旧作面子の登場は控えめ。前のαはGシリーズの中で面白かったけど、こっちはあまり。なんかラストのパートも予想通りだったり。
公式の予定みると最終巻が2023年も出なそうなので、いつか出たら残りのγと3部作をまとめて読むって感じでいいかもしれない。
 
ーーー
その他日記的な。
コンタクトを少し久々に買った。目が色々あって避けてたけど、地味に使いたい場面が多いので。久々に付けると違和感が強め。
 
あとイヤホンが壊れたので同じパナのを購入、まだあって良かった。急に聞こえなくなったので最初は本体かと思ったけど、別のイヤホン試したら大丈夫だった。本体は結構前に販売終了してるので、壊れたら次どうしようかな…今の気に入ってるんだけど、似たような小さいタイプがどこのメーカーも最近無いみたいで。需要ないんだろうか?
 
今月はイベントがほぼ無くて落ち着いてるけど、来月以降のリリイベでどこを頑張るか検討する。薄く申し込んで全部外れるよりは、どこかに集中して確実に行った方がいいというのが最近の気持ち。被って行けなさそうなのは除いて、この辺だろうか。

  • 03/12(日) 小倉唯フォトブック 発売記念イベント
  • 04/08(土) 上坂すみれ13thシングル 発売記念イベント(東京)
  • 04/09(日) 井澤詩織 1st写真集 発売記念イベント
  • 04/15(土) 上坂すみれ13thシングル 発売記念イベント(愛知・大阪)
  • 05/06(土),07(日) にじたび! TOKIMEKI FAN MEETING TOUR 東京公演

すみぺシングルはともかく、他はどれくらい積んだら安全圏なのか良く分からないな。にじたびは広島初日も日程的には何とか行けるんだけど、交通費考えたら10枚単位で他の所に積めるというのがなんか。でも愛知くらいならアリか…

「並行プログラミング入門」読みすすめ その3(入力とか)

「並行プログラミング入門」読みすすめ その1 - NF
「並行プログラミング入門」読みすすめ その2 - NF
3回目、並行プログラミングからちょっと逸れて入力を学ぶ(問題解く用)
本の内容全然関係ないので注意してください。コンパイラはここを使います。
Online Rust Compiler - online editor

超基本

取り合えず教科書通りに書いてみる。

use std::io;
fn main() {
    let mut s = String::new();
    io::stdin().read_line(&mut s).expect("Failed to read line");
    println!("{}", s);
}

入力用の文字列型変数をmutで宣言して、read_lineにで更に&mutを指定して受け取る。
expect()は、関数(今回はread_line)が返すResult型がErrの時にプログラムをクラッシュさせて、指定して文字列を表示する。エラー出さずにクラッシュさせる場合は代わりにunwrap()で良いし、Okしか受け取らない場合はok()で良いらしい。
 

閑話休題

Okしか受け取らないって、実際にErr返ってきたらどうなるの?って思ったので試してみた。

fn foo() -> Result<i32, String> {
    Err("Error".to_string())
}
fn main() {
    foo().expect("Failed to read line");
    println!("{}", "test");
}

まずexpect()の場合、上記コードを実行すると標準エラーに以下が出力される。
expect()呼び出し時点でクラッシュするので、最後の"test"は標準出力されない。

thread 'main' panicked at 'Failed to read line: "Error"', main.rs:5:11

次に、「foo().expect(…)」を「foo().ok()」として実行すると、"test"が標準出力された。
と言う事で、Errは単にもみ消して次に進むっぽい。
 

指定の型で受け取る

本題に戻って、入力をもう少し掘り下げ。
入力値を整数型で受け取りたい場合は、こんな感じ。

use std::io;
fn main() {
    let mut s = String::new();
    io::stdin().read_line(&mut s).expect("Failed to read line");
    let i:i32 = s.trim().parse().ok().unwrap();
    println!("{}", i);
}

空白で区切られた複数の値を受け取って、配列ぽく格納(出力)する場合はこんな感じ。

use std::io;
fn main() {
    let mut s = String::new();
    io::stdin().read_line(&mut s).expect("Failed to read line");
    let v:Vec<i32> = s.trim().split_whitespace().map(|e| e.parse().ok().unwrap()).collect();
    
    for &item in v.iter() {
        print!("{}", item);
    }
}

trim()して空白を取り除いて…と長い、長くない?簡潔に書くやり方がありそうだが、一旦置いておく。
 

実演

ここまでの話の練習として、ABC286のA問題を解いてみる。入力はこんな感じで与えられる。

N
S1
S2
 ⋮
SN

入力だけやってみよう、と思ったら早速間違えました。

use std::io;
fn main() {
    let mut in_str = String::new();
    io::stdin().read_line(&mut in_str).ok();
    let N:i32 = in_str.trim().parse().ok().unwrap();
    print!("{}", N);
    
    for i in 0..N {
        io::stdin().read_line(&mut in_str).ok();
        print!("{}", in_str);
    }
}

これにサンプル2を入力したら、意図せず以下のように出力された。
read_lineの仕様見たら「appending it to the specified buffer.」と書いてあって、append(追加)なのね。

44
2023
4
2023
Year
4
2023
Year
New
4
2023
Year
New
Happy

あと、letで宣言したNを配列宣言の要素数に使おうとすると、constにして、というエラーになるんですが…
これは解決策が分からなかったので、今回は配列を使わないようにして、宿題にする。
 
気を取り直して、多分以下のコードで良さそう。

use std::io;
fn main() {
    let mut in_int = String::new();
    io::stdin().read_line(&mut in_int).ok();
    let N:i32 = in_int.trim().parse().ok().unwrap();

    let mut v:Vec<String> = vec!["".to_string(); 0];
    for i in 0..N {
        let mut in_str = String::new();
        io::stdin().read_line(&mut in_str).ok();
        v.push(in_str.trim().parse().ok().unwrap());
    }
    for item in v.iter().rev() {
        println!("{}", item);
    }
}

ポイントというかハマった所とかはこの辺。
・上で挙げた非const変数を配列数に指定できない
・Vec初期化の書き方が冗長な気がするけどもう少し何とかならないか(Vec::new()で良さそう)
・Vec型の変数(上のv)自体はiteratorじゃないのでrev()が使えない(先にv.reverse()しとくでも良さそう)
 

おまけ:proconio

他の方のACコード見てると、proconioというのがあるのに気付いた。(名前が直接的ですごい)
普通に使えるらしい。以下、まとめてくれている方の記事。
[Rustで簡単標準入力]proconio使い方まとめ - Qiita


ーーー
という訳で超初歩やりました。
全然慣れないので、初期化とか代入とかちょっとした事をやりたくですぐ詰まる。
次回は本の内容に戻りたい

「並行プログラミング入門」読みすすめ その2

「並行プログラミング入門」読みすすめ その1 - NF
2回目、トレイトを理解する

トレイトとは

何はともあれ公式ドキュメント。Docが充実してて助かります。
doc.rust-jp.rs
 
10.2. トレイト:共通の振る舞いを定義する
理解のために適当に書いてみる。基本文法理解も兼ねてるので、凄い冗長な感じになります。
まずは2つの自作の型(struct)を定義。C++で言う構造体・クラス?

pub struct MyInteger {
    pub i: i32,
}

pub struct MyChar {
    pub c: char,
}

fn main() {
    let int : MyInteger = MyInteger{i:1};
    let chr : MyChar    = MyChar{c:'a'};

    println!("Finish.");
}

 
次に、メソッドrep()を持つ自作のRepetitionトレイトを定義して、前述の2つの型に実装する。
rep()は、引数に渡した数だけフィールドの値を繰り返し出力する(謎の)関数とする。

pub trait Repetition {
    fn rep(&self, n: i32) -> bool;
}

impl Repetition for MyInteger {
    fn rep(&self, n: i32) -> bool {
        if n > 0 {
            for i in 0..n {
                print!("{}", self.i);
            }
        }
        true
    }
}

impl Repetition for MyChar {
    fn rep(&self, n: i32) -> bool {
        if n > 0 {
            for i in 0..n {
                print!("{}", self.c);
            }
        }
        true
    }
}

main関数と実行結果。

fn main() {
    let int : MyInteger = MyInteger{i:1};
    let chr : MyChar    = MyChar{c:'a'};
    println!(" {}", int.rep(3));
    println!(" {}", chr.rep(5));
    println!("Finish.");
}
111 true
aaaaa true
Finish.

取り合えず、実装の仕方は分かりました。
インタフェースを決めて、実装はそれぞれの型に任せるって事ですね。
C++に例えると、基底クラスの無い仮想関数って感じでしょうか。

何がうれしいのか

トレイトがあると何が嬉しいのか?
ジェネリックプログラミングで必要になるらしい。
 

ジェネリックとは

と言う事で、公式のジェネリックの説明を読む。(こっちの方が項番先でしたね…)
10.1. ジェネリックなデータ型
C++で言うテンプレートかな?というか、テンプレートがジェネリックプログラミングという概念を実現するために存在する、という感じでしょうか。
 
サンプルに載ってるlargest_xxx()は配列を受け取って最大要素を返す関数。i32版とchar版がある。

fn largest_i32(list: &[i32]) -> i32 {
    let mut largest = list[0];

    for &item in list.iter() {
        if item > largest {
            largest = item;
        }
    }
    largest
}

fn largest_char(list: &[char]) -> char {
    let mut largest = list[0];

    for &item in list.iter() {
        if item > largest {
            largest = item;
        }
    }
    largest
}

fn main() {
    let number_list = vec![34, 50, 25, 100, 65];

    let result = largest_i32(&number_list);
    println!("The largest number is {}", result);

    let char_list = vec!['y', 'm', 'a', 'q'];

    let result = largest_char(&char_list);
    println!("The largest char is {}", result);
}

2つの関数は型が違うだけで同じような処理なので、共通化したい。
ここで、largest()をジェネリックな関数に変更するという事ができる。ただし、下のサンプルだと、コンパイルエラーになる。largestが全ての型に対応してない、というエラーらしい。

fn largest<T>(list: &[T]) -> T {
    let mut largest = list[0];

    for &item in list.iter() {
        if item > largest {
            largest = item;
        }
    }
    largest
}

 
余談:C++で書くとこんな感じだと思います(Tの返し方が分からなかったので、関数内で結果をプリントしてます)。C++のテンプレートは、Rustと違い定義だけではエラーにならない。対応できない型で呼び出す実装を書いて、初めてコンパイルエラーになる。この辺がちょっと違うんですね。

#include <iostream>
#include <vector>

template<class T>
void largest(T&& list) {
    using Type = typename std::remove_cv_t<std::remove_reference_t<T>>::value_type;
    Type largest = list[0];
    for(const auto& item : list) {
        if(item > largest) {
            largest = item;
        }
    }
    std::cout << largest << std::endl;
}

int main()
{
    std::vector<int> number_list = {34, 50, 25, 100, 65};
    largest(number_list);
    
    std::vector<char> char_list = {'y', 'm', 'a', 'q'};
    largest(char_list);
}

 

組み込みトレイトをジェネリック関数に適用(?)

本題に戻って、このエラーを解決するためにトレイトの概念を使う。(10.2.に戻る)
具体的には、以下のようにする。説明は後述。

fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
    let mut largest = list[0];

    for &item in list {
        if item > largest {
            largest = item;
        }
    }
    largest
}

fn main() {
    let number_list = vec![34, 50, 25, 100, 65];

    let result = largest(&number_list);
    println!("The largest number is {}", result);

    let char_list = vec!['y', 'm', 'a', 'q'];

    let result = largest(&char_list);
    println!("The largest char is {}", result);
}

説明。
ジェネリックな引数Tを持つlargest()関数を、特定のトレイトを持つ型だけが使えるように限定することで、エラーにならずに使える。ちなみに、最初に使ったMyIntegerの配列をlargestに渡すと、当然PartialOrd が定義されてないので、コンパイルエラーになる。

   = help: the trait `PartialOrd` is not implemented for `MyInteger`

PartialOrdトレイトは、演算子オーバーロードのために必要らしいが、なんかPartialとかOrdとか数学のアレなようなので理解は後回しにする。このサイト様が参考になりそうでした。
Rust勉強中 - その18 -> 演算子オーバーロード - Qiita
とにかく演算子使うためのおまじないのトレイトで、i32やcharに組み込みで定義されてるので、ユーザが実装しなくて良いみたい。
Copyトレイトは、ローカル変数largestの更新でコピーが発生するので必要。これも同じく組み込みで定義されてる。
 

まとめ

取り合えず、最低限は分かったような気がします。
最初に仮想関数っぽいと書きましたが、Rustもデフォルト実装やオーバライドも可能らしい。サンプルを書き散らして構文が多少身に付いて来た気がします。これ書くだけに結構時間掛かりましたが、手を動かすのが一番ですね…


参考文献