はじめに
Let's encryptのバグの原因はポインタに起因する実装ミスでした。
「Rustはいいぞ」と言うためだけにRustで実装した場合を検証してみます。
原因はなんだった?
詳しくは
https://jovi0608.hatenablog.com/entry/2020/03/09/094737
のステキなまとめを見たほうがいいのですが、
シンプルにすると、このような実装です。
func main() {
	var out []*int
	for i := 0; i < 3; i++ {
		out = append(out, &i)
	}
	fmt.Println("Values:", *out[0], *out[1], *out[2])
	fmt.Println("Addresses:", out[0], out[1], out[2])
}
ValuesもAddressesも[0]~[2]で同じ値が表示されます。
ループカウンタを値渡しではなく、
参照渡しをして保管してしまったことが要因です。
同じような実装ミスをC++で書くとこんな感じです。
# include <iostream>
# include <vector>
int main(){
    
    std::vector<int*> out;
    
    for(int i = 0; i < 3; i++){
        out.push_back(&i);
    }
    
    std::cout << out[0] << std::endl;
    std::cout << out[1] << std::endl;
    std::cout << out[2] << std::endl;
    
    std::cout << *out[0] << std::endl;
    std::cout << *out[1] << std::endl;
    std::cout << *out[2] << std::endl;
    
    return 0;
}
これも同じような結果になります。
Rustで書いてみると
fn main() {
    let mut out:Vec<&i32> = vec![];
    
    for i in 0..3{
        out.push(&i);
    }
    
    println!("{:?}", out);
}
コンパイル時に、このようなエラーが出ます。
6 |         out.push(&i);
  |         ---      ^^ borrowed value does not live long enough
  |         |
  |         borrow later used here
Rustは生存期間を厳密に検証してくれるので、とても安全です!
実際のLet's encryptの実装ミスは、ループの中で関数呼び出しを挟んでいるので
気づくのが難しいとも感じました。
こんなケースではRustは本当に頼もしいです。
Rustはいいぞ
コンパイラにめっちゃ怒られるぞ。
コンパイラに怒られるのに快感を感じるようになったら立派なRust使いです。
