Rust メモ Option | 値を取得して None で置き換える

Option を持つ構造体を扱っているときなどに、Option の値を取得してその後 None で置き換えたいことがある。

そんなときは take メソッド が使える。

Rust Playground

fn main() {
    let mut x = Some(10);

    // Some(v) に対して呼び出すと Some(v) が返ってくる
    assert_eq!(x.take(), Some(10));

    // x は None になってる
    assert_eq!(x, None);
 
    // None に対して呼び出すと None が返ってくる
    assert_eq!(x.take(), None);
}

Rust メモ リテラル

Reference

ここにまとまっている

https://doc.rust-lang.org/reference.html#literals

文字列関連

文字列関連は6つ

type
文字 char 'A' 'A'
文字列 &str "aaa" "aaa"
Raw文字列 &str r"aaa", r#"aaa"# "aaa"
バイ u8 b'A' 97
バイト文字列 &[u8] b"aaa" [97, 97, 97]
Rawバイト文字列 &[u8] br#"aaa"# [97, 97, 97]

Raw文字列リテラル

  • 複数行の文字列を書いたり
  • #を付ければ"をエスケープなしで書くとか
  • \でエスケープさせたくないとか

という時に使える。

整数

整数型は10種類

  • u8
  • u16
  • u32
  • u64
  • usize
  • i8
  • i16
  • i32
  • i64
  • isize

型の名前を数字の後につけると型が定まる。

1u8u8, 10i32i32

型を宣言しているときや型が一意に定まるときは、サフィックスをつけなくても推論される。

任意の位置に_を入れることができる

123_456 == 123456

プリフィックスとしては一般的な感じ

  • 0x で16進数
  • 0o で8進数
  • 0b で2進数

として扱う

浮動小数点数

f32f64

整数と同様に_を任意の場所に入れられる。

整数と同様に型をサフィックスにすることで型を指定でき、指定しない場合は推論される。

値が溢れた場合は型エラーになる。

Rust メモ 文字列

文字列

&strString がある。

通常の文字列リテラルString ではなく &str. ( staticな生存期間を持つので &'static str. )

Raw String Literal

https://doc.rust-lang.org/reference.html#raw-string-literals

いわゆるヒアドキュメント的なもの。

複数行の文字列を書きたいときとか、エスケープが多くなるときに使える記法。

fn main() {
    let s = r"foo
bar";
    println!("s: {}", s);

    let s2 = r#""foo"
"bar""#;
    println!("s2: {}", s2);
}

https://play.rust-lang.org/?gist=570032ed1f2969b4a83bd2c958e61adc&version=stable&backtrace=0

変換

String&str

&str が要求されている場合は、単に &s でいい

fn print_str(s: &str) {
    println!("str: {}", s);
}

fn main() {
    let s: String = "hoge".to_string();
    print_str(&s);
}

https://play.rust-lang.org/?gist=8735d3d12183b7e3af000db46f0213e3&version=stable&backtrace=0

ドキュメントにある通り、 ToSocketAddr など &str に実装されているトレイトが必要な場合は明示的に変換する必要がある。

変換する場合は

  • &*s
  • s.as_str()

が使える

&strString

to_string が使える

let s: String = "hoge".to_string();

文字列 → 数値 (など)

文字列から数値に変換するには、strparse が使える。

変数に型を明示するか、 parse::<i32>() といった形式で型を指定することで特定の型に変換できる。

変換先にできるのは数値に限らず FromStr を実装しているもの。

fn main() {
    let s = "1";
    
    let i = s.parse::<i32>().unwrap();
    println!("i: {:?}", i);
    
    let u: u32 = s.parse().unwrap();
    println!("u: {:?}", u);
}

https://play.rust-lang.org/?gist=1428b04c59de601c19bbc62fbd935bdb&version=stable&backtrace=0

FromStr を実装すれば自分で定義した構造体にも変換できる

use std::str::FromStr;

#[derive(Debug)]
struct Person {
    age: i32,
    name: String,
}

#[derive(Debug)]
struct PersonParseError;

impl FromStr for Person {
    type Err = PersonParseError; 
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let ss:Vec<&str> = s.split(",").collect();
        if ss.len() != 2 {
            return Err(PersonParseError{});
        }
        let age = match ss[0].parse() {
            Ok(v) => v,
            _ => return Err(PersonParseError{}),
        };
        let name = ss[1].to_string();
        Ok(Person{age: age, name: name})
    }
}

fn main() {
    let s = "32,totem";
    let p: Person = s.parse().unwrap(); 
    println!("{:?}", p);
}

https://play.rust-lang.org/?gist=a69a67aa2888fdaad9a811ad712f45ab&version=stable&backtrace=0

整形

出力するだけなら println! マクロや print! マクロ、 文字列として返すなら format! マクロを使う。

フォーマット文字列が printf 系とは違っている。ドキュメントに詳しく書いてある。

std::fmt - Rust

対応はこうなる。 println!("{}", s)format!("{:?}", x) と使う。

  • {}Display
  • {?}Debug
  • {o}Octal
  • {x}LowerHex
  • {X}UpperHex
  • {p}Pointer
  • {b}Binary
  • {e}LowerExp
  • {E}UpperExp

第一引数にフォーマット文字列を渡すが、ここには変数を渡すことはできなくて文字列リテラルしか渡すことができない。

幅についてはパラメータを渡すことができるので動的に変えることができる。

fn main() {
    println!("'{:0width$}'", 10, width = 5);
}

https://play.rust-lang.org/?gist=fe8190d386e7a4ef0282ababa494a6b1&version=stable&backtrace=0

TCPのTail Loss Probeと再送周りについて少し

仕様は RFC にはないっぽくて(?) 2013年にgoogleの方々が出してるinternet draft で定義されている模様。

Tail Loss Probe は、一連の送信パケットの最後のパケットがロスした場合に、送信側が再送タイムアウトを待たずにロスを検知して回復することを目的としている。

パケットがロスした時、後続のパケットが次々と送られている場合はduplicate ackが返されるため、fast retransmitによってリカバリすることができる。

しかし、末尾のパケットや途中から末尾までウィンドウいっぱいのパケットがロスしてしまった場合などは、dup ackが届かないためfast retransmitはトリガーされないため再送タイムアウトまでリカバリできない。

再送タイムアウトを待つと時間がかかりすぎるし、タイムアウトが発生すると輻輳ウィンドウが小さくなってスロースタートからやり直しになってしまう。

そのためなんとか末尾のパケットのロスもタイムアウトより先に検知してリカバリしたいというのが動機。

それをどうやって実現しているかというと、簡単には次のような感じ。

再送タイムアウト(RTO)とは別に、probe timeout (PTO) を定義する。PTOの初期値は先述のdraftで max(2 * SRTT, 10ms) となっており、linuxのRTOの初期値の1秒と比べると短め。

で、データを送信するたびにPTOをスケジュールして、タイムアウトを迎えたらプローブを送信するというもの。

プローブとしては、まだ送っていないセグメントがある場合は新しいセグメントを送り、ない場合は最後に送信したセグメントを再送する。

プローブを送ることで、

  1. 最後のセグメントだけがロスしている場合、最後のセグメントを再送することによってリカバリできる。
  2. 複数のセグメントがロスしている場合、プローブによって他のリカバリの仕組みがトリガーされてリカバリできる。

他のリカバリの仕組みというのは、early retransmit[RFC5827]FACK fast recoveryなど。

early retransmitは、ネットワークに送出されているセグメントが4つ未満である場合、fast retransmitをトリガーするduplicate ackの回数を減らすというもの。

SACKオプションが有効な場合は、(送出中のセグメント-1)回のdup ackを受け取り、かつ最後のセグメントがSACKされている場合は再送する。

例えば最後の2つのセグメントがロスした場合、duplicate ackを受け取ることはないので、TLPがない場合はタイムアウトまで再送されることはない。

TLPがある場合は、PTO後に最後のセグメントを再送するので、それがちゃんと届けば送信側は最後のセグメントがSACKされたdup ackを受け取り、early retransmitにより(dup ackのthreasholdが低くなっているのでfast retransmitがトリガーし)ロスしたセグメントが再送される。

最後の3つのセグメントがロスした場合については1つのプローブを再送しただけではRFC5827のearly retransmitの範囲では再送するには足りないので、最後のセグメントがSACKされたら再送し始めるアルゴリズムを提案している。(proportional rate reduction algorithm。http://conferences.sigcomm.org/imc/2011/docs/p155.pdf

FACK fast recoveryも、同じくduplicate ackの数が少なくても再送しようというもの。

再送する時duplicate ackを3つ以上受け取ったら、となっているのはパケットの順序が入れ替わっただけの場合でもduplicate ackを受け取る可能性はあるからだが、FACK fast recoveryでは順序の入れ替わりであっても抜けが多かったら再送してしまおうというもの。(超ざっくり)

SACKされている最大のack番号と、送信済みでackされていない最小のシーケンス番号の差が3(fast retransmitをトリガーするdup ackの閾値)以上なら再送する。

最後のセグメントを含む4つ以上のセグメントがロスしている場合、再送されたプローブが届いたらこれで再送が始まり、リカバリできる。

ちなみに上記の通り色々な箇所でSACKが使われているので受信者側がSACKオプションを使えることは必須。

ということでTLPでロスをリカバリできるようになる、ということでした。

(主に参照したdraftが古いので、既に古いところはありそう)

アイドルとかにハマる人の気持ちが少しわかった気がする

わからないしわかった気がするというのもおこがましいが少しわかったような気がする。 去年もろくなブログ書いてないが、今年も書く気はなく、しかし今年は俺ももっとぴょんぴょんしていきたい(知らない

Laravel でログのフォーマット変えたり Monolog の Processor 追加したり

もう Laravel から Rails に移行する決意をしたもののめんどくさくて実際は放置、そうしてる間にもログをみることはあって不便だったりして仕方なくいじる。

やりたいのは

  • ユーザがログインしてたらエラーログに user id を extra に追加する
  • extra を表示する位置を先頭付近に持ってくる

の2つ。

公式の ↓ の Custom Monolog Configuration に設定の変更の仕方は載っている

laravel.com

設定を書くのは bootstrap/app.php で、Processor の設定はこのままこれで良さそう。

Monolog がわからないのでフォーマットの変更がよくわからなかったが、↓を参考にした。

blog.muya.co.ke

これだと getMonolog とかしているが、同じ場所でそのまま設定できるので別にファイルを作って...とかはやってない。

bootstrap/app.php で、Monolog のクラスを使うようにするのと、

<?php
use Monolog\Handler\StreamHandler;
use Monolog\Formatter\LineFormatter;

return $app の直前に↓でやりたいことはできた。( extra に user_id 追加するのと、 extra を message の前に出す )

$app->configureMonologUsing(function ($monolog) use ($app) {
    $log_path = $app->storagePath().'/logs/laravel.log';
    $log_stream_handler = new StreamHandler($log_path);

    $log_format = "[%datetime%] %channel%.%level_name%: %extra% %message% %context%\n";
    $formatter = new LineFormatter($log_format);
    $log_stream_handler->setFormatter($formatter);
    $monolog->pushHandler($log_stream_handler);

    $monolog->pushProcessor(function ($record) {
        if (Auth::check()) {
            $user = Auth::user();
            $record['extra']['user_id'] = $user->id;
        }
        return $record;
    });
});

Vue.js の observe あたり雑に追う

(まとまりのなさがひどすぎてアレで放置してたらもう1.0.7になってますはい)

もう 1.0.4 になっとるやないけ

1.0.4 見ますはい。

データバインディングをどう実現しているのかが気になるのでその辺。

データバインディングといえば Observer ということで、 srsc/observer あたりを見てみようかと。

一年くらい前の記事だけど、 JavaScript フレームワークがデータバインディングを実現する4通りの手法 の中で、 Vue.js は「モデル書き換え方式」として紹介されている。

記事の中にもあるように、モデルを書き換えまくることで Vue.js はデータバインディングを実現してる。

おかげで特定のクラスのインスタンスを使わないといけないとかいうことはなく、意識せず使える。

ここから引用すると、 http://vuejs.org/guide/instance.html#Properties_and_Methods

var data = { a: 1 }
var vm = new Vue({
  data: data
})

vm.a === data.a // -> true

// setting the property also affects original data
vm.a = 2
data.a // -> 2

// ... and vice-versa
data.a = 3
vm.a // -> 3

console.log を仕込んでみると書き換えられている様子がわかる。

// インスタンスにセットする前
console.log(data) // -> { a: 1 }

// 後
console.log(data) // -> { a: [Getter/Setter] }

実際に取得できる値に変化はないけど、中身が書き換えられていることがわかる。 [Getter/Setter] という関数(?)になっちゃってる。


Observerhttps://github.com/vuejs/vue/blob/1.0.4/src/observer/index.js#L6L29 ここ。

コメントに、依存関係を収集したり(?)更新をディスパッチするために対象のオブジェクトのプロパティを geter/setter に変換するよ、って書いてある

  • Observer class that are attached to each observed
  • object. Once attached, the observer converts target
  • object's property keys into getter/setters that
  • collect dependencies and dispatches updates.

で、 Dep というオブジェクトが出てくるが、名前的にきっと依存関係を表すものかな。

_.define というので __ob__ というプロパティを対象のオブジェクトに生やして自分自身をそこに入れてる。

_.define は util で、 Object.defineProperty のラッパー

対象が配列なら何とかして(protoAugmentcopyAugment)配列用の便利関数的なものを生やしまくる。

そして walk で実際に convert していく様子

convertdefineReactive を呼び出す。

defineReactive によって、オブジェクトに getter/setter が定義されてる。

getter のほうは、値を返す前に depdepend をたくさん呼んでるが、これはなんだろう。

setter のほうは dep.notify してて、これは更新を伝えるためのものだろう。

Dep がわからんとわからんので src/observer/dep.jsを読む。

メンバ変数としてid, subsを持ってて、 target というクラス変数がある。

https://github.com/vuejs/vue/blob/1.0.4/src/observer/dep.js#L11L19

target には Watcher が入るみたいだがよくわからん。

さっきよく呼ばれてた depend は、targetaddDep を呼び出すもの。

うーん

watcher も見る。

https://github.com/vuejs/vue/blob/1.0.4/src/watcher.js

変更があったら何かを実行するのがこいつの役目。

addDep は自分自身の依存( newDeps, deps )に渡された dep を追加するのと、 depsub に自分自身を追加している

ちょっとこんがらがってきた

Observerインスタンスdep を持っていて、値を get するときに Watcherdepインスタンスdep を追加する

set の時には dep.notify を呼ぶことで、 depsubs を更新して回る。

get の際に addDep したタイミングで 自分のdep.subs にその時点での Dep.target (=watcher) が追加されてる。

nofity で呼ばれる update は、設定によって同期/非同期で値を更新する。

https://github.com/vuejs/vue/blob/1.0.4/src/watcher.js#L209

実際に更新するのはこっちの run メソッドBatcher から実行するためのインターフェースだと書いてある

https://github.com/vuejs/vue/blob/1.0.4/src/watcher.js#L237

ここで watcher 自身の value を更新してコールバックを呼び出している。

watcher を使っている場所を見てみると、 src/directive.js などにある。

https://github.com/vuejs/vue/blob/1.0.4/src/directive.js#L117L129

ここで第3引数が callback なので this._update が Watcher の値が更新されるときに呼ばれる callback ということになる。

this._updatedir.update を呼んでると。

https://github.com/vuejs/vue/blob/1.0.4/src/directive.js#L103L107

update を代入しているのはここ。 descriptor を調べる必要がある。

https://github.com/vuejs/vue/blob/1.0.4/src/directive.js#L79L83

descriptorコンストラクタで引数にとってるので Directive を初期化しているところを見に行く

https://github.com/vuejs/vue/blob/1.0.4/src/directive.js#L34

_bindDir で使われている

https://github.com/vuejs/vue/blob/1.0.4/src/instance/lifecycle.js#L109L113

_bindDir を使っているところはいくつかあるが、わかりやすそうなところでいくとここ

https://github.com/vuejs/vue/blob/1.0.4/src/compiler/compile.js#L395

で、 token.descriptor は何かなというと、雑に見ると恐らく下記

https://github.com/vuejs/vue/blob/1.0.4/src/compiler/compile.js#L360L365

publicDirectives[type] になる。

publicDirectives は何かというと

https://github.com/vuejs/vue/blob/1.0.4/src/compiler/compile.js#L2

var publicDirectives = require('../directives/public')

これなので、 directives/public を見てみる

https://github.com/vuejs/vue/tree/1.0.4/src/directives/public

各種ディレクティブのファイルが置いてある。

簡単そうだから text.js を見てみると、 update が定義されてる

https://github.com/vuejs/vue/blob/1.0.4/src/directives/public/text.js#L11L13

  update: function (value) {
    this.el[this.attr] = _.toString(value)
  }

まさに要素の中身を書き換えているようだ

まだ全体像は見えてないが、なんとなく値が更新された時に DOM にも反映される流れはつかめた。

__ob__ の役割とかよくわかってないな。また後で使いつつ読んでみる。

しかし大変だなー。 Object.observe が使えれば楽なのに