はじめに

Let’s encryptのバグの原因はポインタに起因する実装ミスでした。
「Rustはいいぞ」と言うためだけにRustで実装した場合を検証してみます。

原因はなんだった?

詳しくは

のステキなまとめを見たほうがいいのですが、

シンプルにすると、このような実装です。

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使いです。