読者です 読者をやめる 読者になる 読者になる

テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

(rust report) 標準入力を行ごとに読み込む

最終的に手に入れるべきは, impl<B: BufRead> Iterator for Lines<B>. このためにはBufReadが必要なのだが, io::stdin()で得られるStdinをlockして, StdinLockにしなければ, BufReadを手に入れることが出来ない.

Stdinの説明:

Each handle is a shared reference to a global buffer of input data to this process. A handle can be lock'd to gain full access to BufRead methods (e.g. .lines()). Writes to this handle are otherwise locked with respect to other writes.

lockの説明:

The lock is released when the returned lock goes out of scope. The returned guard also implements the Read and BufRead traits for accessing the underlying data.

なので, こう書けるはずだ!と思って書く. 実際にこれは感じとしては正しい.

for line in io::stdio().lock().lines() {
  ...
}

しかし, 以下のようなエラーによって拒否される.

parse.rs:9:17: 9:28 error: borrowed value does not live long enough
parse.rs:9     for line in io::stdin().lock().lines() {
                           ^~~~~~~~~~~

なぜだ!!!禿げてしまう!考える. エラーをぐぐると色々と親切な回答が出てくるのであるが, ぱっとしない. 私は, 「Rustはこのコードをこういうモデルを用いてこう解釈する. だからダメなのだ」という説明を得なければ納得出来ない.

使っている関数は以下の3つ.

  • pub fn stdin() -> Stdin
  • fn lock(&self) -> StdinLock
  • fn lines(self) -> Lines

全くの推測だが, Rustは以下のような考えでこのメソッドチェインを拒否してるのだと思う.

  • 返り値はテンポラリである. (Rustはスタックのデータに対して寿命を定義しているので, スタックにないものは寿命が定義出来ないのだと思う. 実装自体もおそらくそうなっているのではないか?スタックが死ぬ時にスタック上のデータをDropするのだから, 何か参照を持っているべきだし, きっと「スタックにあるデータ」という名前のクラスが定義されているはずだ!!!<-コードを読んでないので勘)
  • 寿命のないテンポラリを参照で又貸ししてはならない. それは, スタック上にテンポラリの変数を参照で返すuse-after-freeに等しい行為である.
  • 従って, Stdinをlock(&self)に渡してはならない.

なぜ, lock()で拒否されていると考えるかというと, このコードの修正方法が,

let stdio = io::stdio()

を切り出して一旦完全な変数によってlifetimeをテンポラリより長くすることであり,

let lock = io::stdio().lock()

のように切り出した場合は, やはり同様のエラーによって拒否されるからである. (ようするに, 切り出す部分で比較して, どこでエラーが起こったか実験的に特定している)

教訓

たぶん, Rustのコーディングスタイルとしては,

  • それっぽいコードを書く
  • 怒られる
  • コンパイラのおっしゃるとおりに修正する

を繰り返すものなんだろう. いきなり正しいコードを書くことは諦めた方がいい.

ところで, 特に寿命に関するエラーは, (もともとコンパイラが持っている情報だし)機械的に修正出来るはずなので, 修正出来るならば自動的に修正して欲しい.

辛い.