WSL で有効なコマンドの alias を巧く張る方法のメモ
↓こんな感じで alias を仕込むと WSL 環境でのみ alias を張れます:
if [[ $(grep -i Microsoft /proc/version) ]]; then alias code="/mnt/c/Users/usagi/AppData/Local/Programs/Microsoft\ VS\ Code/bin/code" alias display.blank="/mnt/c/Windows/System32/scrnsave.scr /s" alias power.sleep="/mnt/c/Users/usagi/app/PSTools/psshutdown.exe -d -t 0 -accepteula" alias power.hibernate="/mnt/c/Windows/System32/shutdown.exe /h" alias power.reboot="/mnt/c/Windows/System32/shutdown.exe /r" alias power.logoff="/mnt/c/Windows/System32/shutdown.exe /l" alias power.shutdown="/mnt/c/Windows/System32/shutdown.exe /s" fi
↑の例では:
/proc/version
を見て WSL を判定して$HOME
の環境を複数のPC、異なるOSで共有している人には管理コストをひとまとめにして分岐を書くのも便利な方法の1つと思います
code
(VSCode)を host 側で起動する (ホストWindows環境のパスを継承する設定で使っている場合は要らないやつです)display.blank
はブランクスクリーンのスクリーンセーバーを起動する事で、画面を一時的にすべて黒くしたい時に使うコマンドです- note: 写真撮影するときに画面が映り込んだり色が狂うのが嫌な時に便利です
power.*
系のコマンドは電源制御のお馴染みのコマンド群です。寝る前はたいていpower.sleep
/proc/version
WSL だと↓のように microsoft
が入るので判定に使用できています:
Linux version 4.19.84-microsoft-standard (oe-user@oe-host) (gcc version 8.2.0 (GCC)) #1 SMP Wed Nov 13 11:44:37 UTC 2019
WSLの判定方法は他にも複数思いつきます。わりとなんでもよいと思います。
Rust で XML パーサー使いたいならどの crate を使うと嬉しいかもしれないか、のメモ
状況
crates.io には執筆時点では複数の XML お取り扱いらしい crate が存在しています:
- xml-rs 4,013kDL
- quick-xml 681 kDL
- xml5ever 183kDL
- xmlparser 232kDL
- sxd-document 63kDL
- strong-xml 0.7kDL
- trashy-xml 0.7kDL
などなど。他にもいくつも登録されています。
1つの性能指標: quick-xml vs. sxd-document vs. xml5ever vs. xml-rs
quick-xml, sxd-document, xml5ever, xml-rs の4つについて性能指標を計測する crate があるようです:
note: RazrFalcon/choose-your-xml-rs では RazrFalcon/roxmltree#alternatives を事実上の移行先として案内していますが、残念ながら RazrFalcon/roxmltree は git clone
からとりあえず cargo bench
したところ大量のエラーでビルドできず面倒くさい気配がしたのでベンチマークとしては触れない事にしました。( roxmltree 本体の cargo build
は問題ないのだけど )
リポジトリーは既に archived ですが動作は可能でした。とりあえず clone
してそのまま試してみると:
cargo bench # xml-rs-0.7 quick-xml-0.10 xml5ever-0.11 sxd-document-0.2
running 11 tests test quick_xml_large ... bench: 1,922,040 ns/iter (+/- 53,752) test quick_xml_medium ... bench: 519,440 ns/iter (+/- 20,166) test quick_xml_small ... bench: 8,187 ns/iter (+/- 262) test sxd_document_medium ... bench: 2,767,680 ns/iter (+/- 295,128) test sxd_document_small ... bench: 46,143 ns/iter (+/- 6,440) test xml5ever_large ... bench: 9,244,405 ns/iter (+/- 629,496) test xml5ever_medium ... bench: 7,367,160 ns/iter (+/- 1,179,293) test xml5ever_small ... bench: 53,068 ns/iter (+/- 11,841) test xmlrs_large ... bench: 25,043,130 ns/iter (+/- 1,684,648) test xmlrs_medium ... bench: 11,846,650 ns/iter (+/- 2,163,808) test xmlrs_small ... bench: 87,464 ns/iter (+/- 16,859)
↑は依存が最新版に設定されていないので:
依存する XML crate 群を最新版に再定義し、 quick-xml と xml5ever のバージョンアップに伴う仕様変更に対応するパッチを充てて:
cargo bench # xml-rs-0.8.3 quick-xml-0.18.1 xml5ever-0.16.1 sxd-document-0.3.2
running 11 tests test quick_xml_large ... bench: 1,959,025 ns/iter (+/- 109,147) test quick_xml_medium ... bench: 510,790 ns/iter (+/- 87,736) test quick_xml_small ... bench: 7,367 ns/iter (+/- 154) test sxd_document_medium ... bench: 2,809,025 ns/iter (+/- 323,460) test sxd_document_small ... bench: 44,845 ns/iter (+/- 1,459) test xml5ever_large ... bench: 8,118,040 ns/iter (+/- 569,471) test xml5ever_medium ... bench: 6,755,910 ns/iter (+/- 168,634) test xml5ever_small ... bench: 47,349 ns/iter (+/- 2,106) test xmlrs_large ... bench: 24,848,280 ns/iter (+/- 1,742,538) test xmlrs_medium ... bench: 12,093,030 ns/iter (+/- 978,933) test xmlrs_small ... bench: 92,197 ns/iter (+/- 6,255)
巨大なXMLを扱う、パース速度が大事、そのような場合はこれらの選択肢から選べば quick-xml がとても優秀っぽい事がわかります。
minidom ≈ quick-xml vs. roxmltree ≈ xmlparser
xmlparser は↑のベンチマークの Author の RazrFalcon が書いた crate です。↑のベンチマークで"事実上の移行先"としてリンクされていた roxmltree
は xmlparser
をラップした高レベルパーサーという位置づけのようです。ここでちょっとした XML パーサー crate 群の整理:
high | low |
---|---|
roxmltree | xmlparser |
sdx-document | (独自の実装詳細) |
xmltree | xml-rs |
minidom | quick-xml |
↑こんな高レベルのパーサーと低レベルパーサーの関係になっていたようです。その上で、巨大な XML を大量を扱いたい場合にはやはり速度は大事なので、 quick-xml vs. xmlparser あるいは minidom vs. roxmltree を中心に比較すると、
- パース速度:
- 高レベルのパース: roxmltree(xmlparser) が minidom(quick-xml) より 1.59 倍くらい高速
- 低レベルのパース: quick-xml が xmlparser より 1.35 倍くらい高速
- 列挙速度:
- 任意要素の文字列マッチング: xmltree が roxmltree より 1.07 倍くらい高速、 minidom より 1.79 倍くらい高速
- 特定名要素の検索: romxltree が xmltree より 4.58 倍くらい高速、 minidom より 5.86 倍くらい高速
らしい。また、 roxmltree について RazrFalcon によると:
- xmlparser が quick-xml より遅い部分はより厳密なパースによるもの
- roxmltree は設計上は
panic
を起こさないし内部でunsafe
も使わない - roxmltree では XPath/XQuery, 変更や書き出し、仕様上完璧なXMLのサポートをする気は無いよ
と README に明記されています。
とりあえずの結論
- 高速な低レベルパーサーが必要な場合: quick-xml または次点で xmlparser
- 高速で安全安定っぽい高レベルサーバーが必要な場合: roxmltree
- もし特定の機能サポート都合で roxmltree ≈ xmlparser を使い難い場合:
- 高レベル向け: xmltree, minidom, sdx-document, または別の何かを探すか作るか
- 低レベル向け: quick-xml, xml-rs, または別の何かを探すか作るか
実際に使ってみてより詳細な気づきがあればその時にまたメモを追加しようと思います。
参考
VSCode via WSL2 で rust-analyzer が OUTPUT に Assertion failed: We don't expect to receive commands in CodeActions エラーを盛り盛り上げてきたら思い出すメモ
症状
WSL2 経由で動作する VSCode の rust-analyzer が一見動作しているように見えて OUTPUT に大量のエラーメッセージ:
Assertion failed: We don't expect to receive commands in CodeActions
を吐く。発症するとエラーが吐かれる度、作業中やログ監視などしている TERMINAL や PROBLEMS のタブからから OUTPUT タブへ表示も切り替わり不便です。
解決方法
さしあたり rust-analyzer
を手インストールすると発生しなくなるかもしれません:
cargo install rust-analyzer --git https://github.com/rust-analyzer/rust-analyzer/
参考
Rust で cdylib/wasm を吐く crate を分割したら依存先の機能を呼べなくなり、なんとなく extern crate を明示してみたら can't find されて5分くらい悩んだメモ
だいじな事:
crate
を分割したら、お呼ばれされる側の Cargo.toml で[lib]
のcrate-type
が明示的にrlib
を吐かない定義になっていないか確認しよう!
期待動作する例
# crate aaa に依存される側の crate bbb の Cargo.toml # ☆ ↓ src/lib.rs ありの crate では書かなくても同義扱いなのだけど、今回のメモの本質的な部分なのであえて明示しました。 [lib] crate-type = [ "rlib" ]
# crate bbb をに依存する側の crate A の Cargo.toml [dependencies] bbb = { path = "../bbb" }
// crate bbb に依存する crate aaa の main.rs // ◎ Rust を edition = "2018" で使う場合は extern carate は不要です; あっても問題ないけど extern crate bbb; // ◎ use しなくてもシンボルへの完全なパスを書けば使えます use bbb::some_module::some_sub_module::awesome_feature; // ◎ crate bbb に分割した何かを使う的な模擬コード let my_hoge = awesome_feature::hoge();
5分くらい悩んだダメな例
# crate aaa に依存される側の crate bbb の Cargo.toml [lib] crate-type = [ "cdylib" ] # ☆ rlib 出力が無いと依存してくれる側の .rs から extern して密結合できないのです。うっかり
解説
分割前の crate が .wasm を吐くとか、 .so/.dll/.dylib 的なそれを吐くのがプロジェクト単位での出力の場合、 [lib]
で crate-type = [ "cdylib" ]
とか定義しているはずです。そのような aaa から bbb を分割する際に、 Cargo.tml の内容を aaa の複製を元に書き出し、 cdylib
しか出力しない crate bbb を定義してしまうと、 crate aaa から [dependencies]
で依存する事はできますが、rlib
が無い状態では rust のソースコードから extern
して密結合的に使う事はできません。 crate bbb が cdylib
を出力する定義では crate aaa のビルドでも .wasm あるいは .so/.dll/.dylib 的なそれはビルドされます。その出力「も」欲しい場合もあるかとは思いますが、今回は crate aaa を整理のために crate bbb と分割し、 crate aaa から crate bbb へ依存するのが目的のため rlib
出力を追加定義または rlib
出力のみに変更するのが期待動作する分割に必要です。
React で TypeScript な Component 内の一部のメソッドから他の定義済みのはずのプロパティーや state へのアクセスが undefined になった時に思い出したいメモ
期待動作する例
↓は button をポチると hidden で置いてあるファイルアップロード用の input の click を発火するコンポーネント的な例です。
import React, { Component } from "react"; interface IProps { } interface IState { } export default class FileUploader extends Component<IProps, IState> { // ↓ TypeScript で refs 的なことをしたい場合に増える仕込み private fileUploader:React.RefObject<HTMLInputElement>; constructor(props:IProps) { super(props); // ↓ ref る仕込み this.fileUploader = React.createRef<HTMLInputElement>(); this.state = { my_awesome_value: undefined }; } // ↓こう書くと this.fileUploader など期待動作する // あえて method{} ではなく property = closure な定義方法をしているところが核心です handleClick = (): void => { // 無事 ref れていれば input 要素の click を発火できてファイルオープンダイアログが出てくれるところ this.fileUploader.current?.click(); // note: この handleClick の定義方法の場合は state へのアクセスも期待動作します } render() { // ↓ ref= に仕込み return ( <div> <button onClick={this.handleClick} /> <input type="file" id="file" ref={this.fileUploader} style={{ display: "none" }} /> </div> ); } }
ダメな例
// ↓メソッドの定義方法を一見シンプルなただのよくあるメソッド的に記述すると… // handleClick 自体は button の onClick から呼ばれてくれますが… handleClick() { // ↑このメソッドの定義方法だと↓でfileUploader が undefined で runtime に死ぬ this.fileUploader.current?.click(); // note: この handleClick の定義方法の場合は state へのアクセスも undefined で runtime に死にます }
React/TypeScript初心者にはしばらく何が起きているのか脳内が?で満たされながらの状況調査となりましたがわかってしまえば JavaScript 界隈ではしばしば遭遇する類のトラブルだったようです。
参考
wasm-bindgen で fetch する rustwasm 公式の example の async で JsFuture な run が実行時に失敗した場合にも安全に対応できるようにする方法のメモ
問題
↑
- この example の
run
にrustwasm/wasm-bindgen
のような、実装上GitHub APIで取得可能なポジトリーが実在する場合は問題ありません。 - リポジトリーが存在しないパターンを
run
に与えてfetch
させるとasync
/JsFuture
処理系の都合か二度とrun
を使用できない .wasm が生成されてしまいます。
問題は example の run
の最後の2行:
// Use serde to parse the JSON into a struct. let branch_info: Branch = json.into_serde().unwrap(); // Send the `Branch` struct back to JS as an `Object`. Ok(JsValue::from_serde(&branch_info).unwrap())
解決方法
match
で json
の Err<serde_json::error::Error>
を Err<JsValue>
に射る:
// 解決方法①段階 match json.into_serde() as Result<Branch, serde_json::error::Error> { Ok(branch_info) => Ok(JsValue::from_serde(&branch_info).unwrap()), Err(e) => Err(JsValue::from_str(&format!("{}", e))), }
↑だとまだ json.into_serde()
は Ok
だけど JsValue::from_serde
が Err
だった場合は死んでしまいますが、 GitHub API は実在しないリポジトリーに対しても JSON を返してはくれるので、さしあたりは問題にはなりません。そこも match
すると↓:
// 解決方法②段階 match json.into_serde() as Result<Branch, serde_json::error::Error> { Ok(branch_info) => match JsValue::from_serde(&branch_info) { Ok(jsvalue) => Ok(jsvalue), Err(e) => Err(JsValue::from_str(&format!("{}", e))), }, Err(e) => Err(JsValue::from_str(&format!("{}", e))), }
↑のそこはかとない多段 match
のダサさを Result
の map
と map_err
で整理すると:
// 解決方法③段階 (json.into_serde() as Result<Branch, serde_json::error::Error>) .map(|branch_info| JsValue::from_serde(&branch_info).unwrap()) .map_err(|e| JsValue::from_str(&format!("{}", e)))
- (1.
json.into_serde()
):json.into_serde()
:=Ok<Branch>
かErr<serde_json::error::Error>
です - (2.
map
): (a) がOk
の場合にmap
でfrom_serde
の結果をunwrap
:=Ok<JsValue>
またはErr<serde_json::error::Error>
です - (3.
map_err
): (a) または (b) の何れかがErr<serde_json::error::Error>
の場合はmap_err
でErr<JsValue>
に変換されます
こうすると run
の内部で Err<serde_json::error::Error> が発生しても Uncaught (in promise) missing filed `name` at line 1 column 104
のような console.error
が吐かれるだけで、 run
が使用不能には至らなくなります。
この他にも unwrap()
により panic が発生する可能性がある部分はありますが、 GitHub が 404 になったり JSON レスポンスを廃止しない限りは事実上死には至らないです。学習用途ではなく実用する場合はより厳密に async
/JsFuture
の絡む内部で panic が発生しないように気にする必要はあります。
諸事情により React で TypeScript な web アプリの中から window に global な変数を write access する方法のメモ
Window
をオレオレ派生した interface
をでっちあげてキャストすれば window
の任意のプロパティーに write access できる。後は野となれ山となれ。
↓例、React の state 持ちの Component な App のインスタンスを window.app
な global 変数として登録:
App.tsx:
// 凡例 ☆ := 本質的な部分 // 凡例 ◎ := おまけ, state を扱う部分 // ☆Window を派生して app 変数を持ったインターフェースをでっちあげる interface IGlobalWindow extends Window{ app?: App; } // ◎ これらは constructor で state を定義したい場合に必要なインターフェース interface IProps {} interface IState { aaa?: any; } export default class App extends Component<IProps, IState> { constructor( props: IProps ) { super(props); // ◎ ctor ではトリアエズ {} な aaa を this: App の .state 放り込んでおいてみます this.state = { aaa: {} } // ☆ window をオレオレ派生したインターフェースに明示的にキャストした上で .app 変数に書き込み; このメモの本質的な部分です (window as IGlobalWindow).app = this; } componentDidMount() { // ◎ 適当なタイミングで App の state を設定してみます this.setState( { aaa: 123 } ); } }
↑こうすると、ウェブブラウザーの inspector ツール的なそれの Console からも↓のように App のインスタンスやそのプロパティーにアクセスできるようになります:
> app.state.aaa < 123
おまけメモ: React Developer Tools
- Firefox https://addons.mozilla.org/ja/firefox/addon/react-devtools/
- Chrome https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi
- Edge https://microsoftedge.microsoft.com/addons/detail/react-developer-tools/gpphkfbcpidddadnkolkpfckpihlkkil
これをブラウザー側へ入れられる場合は、導入後に React のページを読み込むと $r
で React の Root にぶちこんだ App
のインスタンスにアクセスできるようになります。今回のメモの主題とは異なりますが、外から中を見たいだけなら素直に React Developer Tools を入れればよいです。