Rust: イテレータメソッドの by_ref() と take_while() の組み合わせには気をつけようという話(てかドキュメントを読もう)

事の発端

take_while というイテレータメソッドがある。 doc.rust-lang.org 詳しい説明は公式に任せるとして、 take_while した後の残りが欲しかったが take_whileself を取るので 一回使ったら再利用できない。by_ref を使ってrestを取ろうとした。が、take_whileイテレータが止まる条件を満たしたであろう最後に舐められた要素はdropされるという話でした。 例は下の playground で

play.rust-lang.org

まとめ

この記事書いてる時に

Because take_while() needs to look at the value in order to see if it should be included or not, consuming iterators will see that it is removed

の言及に気づいたのでドキュメントはちゃんと読みましょう

オチ

Option<Self> を返す trait を実装したかったがRustコンパイラ君が賢すぎただけの日記

trait OptionalConstructor {
    fn optional_new() -> Option<Self>;
    //                   ^^^^^^^^^^^^ doesn't have a size known at compile-time
    //                   Option<Self> where Self: Sized;
}
   Compiling playground v0.0.1 (/playground)
error[E0277]: the size for values of type `Self` cannot be known at compilation time
   --> src/main.rs:2:26
    |
2   |     fn optional_new() -> Option<Self>;
    |                          ^^^^^^^^^^^^ doesn't have a size known at compile-time
    |
help: consider further restricting `Self`
    |
2   |     fn optional_new() -> Option<Self> where Self: Sized;
    |                                       ^^^^^^^^^^^^^^^^^

記事にしようと思ったけどコンパイラ君が丁寧に書いてくれたので特に書くことなかった...、Self が lifetime parameterを取る場合もこのように書ける

trait OptionalConstructor {
    fn optional_new() -> Option<Self> where Self: Sized ;
}

struct S<'a> {
    s: &'a str
}

impl<'a> OptionalConstructor for S<'a>{
    fn optional_new() -> Option<Self> {
        None
    }
}

fn main() {}

ちなみに自分でOptionもどきを実装しても同じような事を言われる、Rustコンパイラ君賢すぎる...

Rust: Fully Qualified Syntax について簡単なメモ

2021/3/31 更新

高度なトレイト - The Rust Programming Language 日本語版

を知らなかった、これを読んでください

経緯

数時間前に「僕はチンパンジーだからでパーサコンビネータ使うの難しいな〜、LR(1)パーサを手書きするのが一番早いですよ〜」と同僚と会話をしていたid:Krouton です。フルスクラッチでやる縛りを設けてないので今回実装するパーサはパーサジェネレータを使おうかなと考えていました。チンパンジーだから分からん、と逃げていても一生進まないので nom の使い方を調べていたら友人のエントリにヒットして読んでたらFully Qualified Syntaxという知らない概念に出会いました。

drumato.hatenablog.com

該当エントリ内に書いてあるとおり公式を読めば*1終わりっちゃ終わりなのですが、ググって日本語の記事がないのも寂しいのでパーサのモチベが無くならない程度に書きます。

何が嬉しいのか

impl した trait のメソッド名が他の impl と被った時にどの trait のメソッドを呼び出すのか一意に定める事ができます。

続きを読む

inkwellにPR投げてマージされた

inkwellって何

RustのLLVMバインディングです、IRをいい感じに吐き出すと思います。*1

該当のPR

Refactor: use matches! in is_xxx functions instead of if-let expression. by Krout0n · Pull Request #232 · TheDan64/inkwell · GitHub

*1: まだ使ったことなくて、調べごとでコードリーディングをしたら見つけた

続きを読む

また serde-rs/json にPR投げてmergeされたので Value::pointer / Iterator::try_fold のおすすめメソッド2選についてメモ

また?

前回のです serde-rs/json にPR投げてmergeされた - KRAZY感情STYLE

今回のPR

github.com

Value::pointer とは

pointer を使わずに obj[x][y] を取ってくる例を考えてみます

use serde_json::{Value, json};

let obj = json!({"x": {"y": 1}});
let value = if let Value::Object(obj) = obj {
    let x = obj.get("x");
    if let Some(Value::Object(child_obj)) = x {
        child_obj.get("y")
    } else {
        None
    }
}  else {
    None
};
assert_eq!(value, Some(json!(1)));

こんな感じにわざわざ enum のvariantを剥がす処理を何度も書きます、地獄ですね。Value::pointer を使うと

use serde_json::{Value, json};

let obj = json!({"x": {"y": 1}});
assert_eq!(obj.pointer("/x/y"), Some(json!(1)));

このようにネストしたオブジェクトでもシュッと取ってこれます。前回PRを投げるきっかけになったRFC*1で記述されているpathと同じ記法*2で取ってくることができます。便利。 このメソッドに気づかずに自作しようと思ったのですが、上手く実装できなかったので同様のRFCを実装しているリポジトリ*3 を見たらこのValue::pointerを使っていました、偉い。

Value::pointer の実装を読んだ

自作できなかったのは心に来たので実装を読みました。最初実装に失敗した時は Iterator::fold を使おうとして失敗してたのですがforループで実装されていたので「ア〜〜〜〜」となりました。でもfold でもできそうな気がしたのでやったらできました、高階関数?関数型?に圧倒的感謝! しかし、fold だと /a/b/c/d/e/f/g/h/i みたいなケースで /a がなくても /b/c などを見に行きます、これが1万個のネストしたキーとかだったら辛いですね。 そこでIterator::try_fold を使うといい感じにearly-returnしてくれるっぽいです。

総括

ドキュメントにこんな乱文よりいい感じの説明があるので読みましょう

serde-rs/json にPR投げてmergeされた

serde-rs/jsonって何

Rustで言うJSONを扱うライブラリの中で一番勢いあるやつ

実際のPR

一発mergeされて嬉しかった github.com

経緯

  • RFC 6902 - JavaScript Object Notation (JSON) Patch を読んで実装したくなる
  • 中のMapがEntryをサポートしてることに気づく
  • 標準ライブラリではEntry::and_modify がサポートされてるがserde-rs/jsonでは無い事に気づく
  • いきなりPR投げてmergeされる
  • RFC6902の実装がめんどくなる <- イマココ!!

所感

OSSにPR投げる時ってギョームで「こういうutil関数あったら便利じゃん!」って言って気軽に投げるノリで投げて良さそう、てかギョームじゃん