dackdive's blog

新米webエンジニアによる技術ブログ。JavaScript(React), Salesforce, Python など

2020年のふりかえりと2021年にやりたいこと

去年もふりかえりしてたので今年も書く。

去年:

今年の四半期ごとのふりかえり:

年初に立てた目標

去年の今ごろには

  • 英語ブログ、今年こそ書く!目標は3本
  • 今年もどっかのカンファレンスにCfP出す
  • 「みんなのデータ構造」とあともう1冊、コンピュータサイエンスに関する本を読む
  • 本は年間で6冊読む

という目標を立てていたらしい。
そのうち、実践できたのは最後の「本は年間で6冊読む」だけ(ひどい)

英語学習や勉強会への登壇に対するモチベーションが途中から完全になくなってしまっていた反面、
Rust とか新しいことにチャレンジできたのは良かった点かな、と前向きに捉えることにする。

2020年をふりかえって

技術面

ざっくり、1年の前半はデータ構造とアルゴリズム、後半はRust、WebAssemblyそして最後にOAuth 2.0という感じだったはず。

去年〜今年4月ごろまで読んでた。
これは正直自分にとってはめちゃくちゃ難しかったんだけど、読んで良かったと思える本だった。

これに続く形でデータ構造とアルゴリズム関連の書籍を読み進められなかったのは反省点。

その後、5月〜7月あたりに読んだのがこの本。
まだまだ勉強不足で Rust は全然わからない。でも Rust 勉強してみたいな〜と思ってたので一歩踏み出せてよかった。

Rust を勉強したんだから WebAssembly にコンパイルして動かす方法も知っておきたい、と思って読んだのがこのあたり。
最初 Tour of WebAssembly 読んだけどさっぱりだった。

それから、今年の最後の方は今まで雰囲気で理解していた OAuth 2.0 を学び直していた。
本を読んだり RFC を読んだりすると新しい学びがある、というか OAuth のこと全然理解してなかったんだなーというのがわかってよかった。今もちゃんと理解できてない。

OAuth 2.0 に関しての学びは、つい先日 Zenn の方にアウトプットできた。

この記事を書く過程で、アウトプットってやっぱ大事だなーと痛感した。
書きはじめてから公開までなんだかんだ1ヶ月ぐらいかかってて、その間書こうと思ったらちゃんと理解できてないことに気づいて調べ直して...というのを何度も繰り返した。

生活面

今年はコロナによって働き方が大きく変わった一年だった。
小さい子供が2人いるので、これまでは在宅勤務なんて絶対無理と思って毎日通勤してたんだけど
コロナで強制的に在宅勤務を余儀なくされた結果、今ではすっかり適応できている。

それこそ在宅勤務がスタートした当初(3月ぐらい)は

  • 子供がうるさくて仕事に集中できない
  • 椅子が安物で疲労が溜まる、腰が痛くなる
  • 通勤時間がなくなって一人ぼーっとする時間がとれず仕事とプライベートの気持ちの切り替えができない

、、、などなど相当辛かった記憶があるが、少しずつ新しい働き方に適した生活リズムを作ることができて、ストレスなく仕事できる状態にはなった。
逆に、夕飯や風呂の時間を家族と過ごせるという在宅勤務のメリットは非常に大きいことにも気付かされ、以前の、片道1時間以上かけて通勤してた生活には戻れないと思う。

一方で、在宅勤務に慣れた9月ごろから、マンネリ感というかなんとなく毎日可もなく不可もなくという状態が続くようになったと感じている。
モチベーション的に大きく下がることもないが上がることもない、みたいな感じ。 これはたぶん、コロナによって行動が制限され、同僚と物理的に会ったり飲み会のようなコミュニケーションの機会がなくなったことや、毎日代わり映えのしない場所で仕事を続けることになったせいで、(ただでさえ少なかった)日常のメリハリが失われていることが原因だと思う。

これまではコロナ禍でも以前と変わらず仕事ができていることに感謝していたけど、今後は「どうやったらもっと楽しくなるか?」を考えて、意識的にアクションをとらないとこのままの状態がずるずる続きそう。

その他

四半期ごとにふりかえりを書くようにしたのはよかった。
1年をふりかえろうとしたときにだいたい当時何やってたか書いてあるし、あと目標管理のサイクルとして3ヶ月で達成できそうな目標を立てるのは自分には合ってそう。
興味がコロコロ変わるので1年前にやると決めたことをやるのはハードルが高すぎる。

2021年にやりたいこと

新たになにか始めたいというよりは、今年スタートだけはできたコンピュータサイエンス系の勉強やRust & WebAssembly の勉強を継続できるようにしたい。
あとは、意識して質の低いアウトプットを増やすことをやっていきたい。

コンピュータサイエンス(あるいは、低レイヤの勉強?)

今年のはじめにコンピュータサイエンスを勉強したいと思ったときは、そもそもコンピュータサイエンスという学問の中にどういった分野があるのかよくわかっておらず、ただその中でもデータ構造とアルゴリズムが一番イメージしやすく取り組みたいことだったので手を出した。

今は、データ構造とアルゴリズムに加えて、いわゆる低レイヤと呼ばれる領域に興味がある。
コンパイラとかOSとかを手を動かして作ってみたい。

というわけで12月後半にこの本を買って、Rust でやってみようとしている最中。

大学行きたい欲も依然としてあるものの、少なくとも来年は興味を持った領域を書籍などで自分のペースでやるのがいいんじゃないかなとも思っている。

leetcode

前述したように、データ構造とアルゴリズムについて書籍などで体系的に学ぶ意欲が薄れてきているので、今年の後半ほとんどできてなかった leetcode を来年は継続できるようにしたい。
曜日を決めて取り組むようにしてうまくリズムを作りたい。

Rust & WebAssembly

どちらもまだまだ勉強し始めたばかりというレベルなので、継続したい。
Rust は低レイヤの勉強とうまくからめて書くきっかけを作れるといいな。WebAssembly は今のところ作りたいものが特に思いつかないので、
本を読むのがいいんじゃないかと思ってる。

質の低いアウトプットを増やす

これは技術領域ではなく心構えの話なんだけど、年末にこちらのブログを読んで。

すごい共感できて、自分もここのブログは今まで以上に「駄文でも誰の役にも立たないかもしれなくてもとりあえず書く」を意識しようと思った。
雑なアウトプットはこちらで、もうちょっと他人に読まれることを意識したものは Zenn で、みたくうまくプラットフォームを使い分けたい。

Rustのモジュールシステムの基本

久しぶりに Rust を書いたらモジュール周り全然思い出せなかったのでメモ。
以下の公式ドキュメントの第7章を自分用にまとめつつ、調べたことを足した内容になります。

The Rust Programming Language 日本語版 7. 肥大化していくプロジェクトをパッケージ、クレート、モジュールを利用して管理する

パッケージ、クレート、モジュール

  • パッケージ:1つ以上のクレートを持ち、ある機能を提供するための単位
    • 1つの Cargo.toml で管理する粒度
    • 0個か1個のライブラリクレートを持つ
    • バイナリクレートはいくらでも持って良い
    • ライブラリクレートとバイナリクレートをあわせて少なくとも1つのクレートを持っていないといけない
  • クレート:ライブラリ(lib.rs)か実行可能ファイル(main.rs)を生成するモジュール群
    • ライブラリクレート: src/lib.rs
    • バイナリクレート: src/main.rs
      • または、 src/bin ディレクトリにファイルを置くことで複数のバイナリクレートを持つことができる
  • モジュール: moduse でまとめる単位

ので、ディレクトリ構成としては以下のようになる(後述するモジュール単位のファイルを除く)

<package>
  - Cargo.toml
  - src/
    - lib.rs     ... 0 or 1
    - main.rs    ... 0 or 1
    - bin/       ... 0 or N (files)
      - bin_a.rs
      - bin_b.rs
      - ...

モジュールの基本的なルール

mod キーワードでくくった範囲がモジュールとなり、スコープが分かれる

mod front_of_house {
    mod hosting {
      fn add_to_waitlist() {}
    }
}

mod back_of_house {}

これは、以下のようなモジュールツリーを形成する。

crate
  - front_of_house
    - hosting
      - add_to_waitlist
  - back_of_house

モジュールはパスを指定して呼び出す

mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}
    }
}

pub fn eat_at_restaurant() {
    // 絶対パス
    crate::front_of_house::hosting::add_to_waitlist();

    // 相対パス
    front_of_house::hosting::add_to_waitlist();
    self::front_of_house::hosting::add_to_waitlist();
}

モジュールはデフォルトで非公開。公開するには pub キーワードをつける

  • モジュールの公開範囲と、モジュールの中身の公開範囲は別々に指定する
mod front_of_house {
    pub mod hosting {
        // ここもpubにしないとadd_to_waitlist()関数にはアクセスできない
        pub fn add_to_waitlist() {}
    }
}


  • 構造体は各フィールドを個別に公開するかどうか決められる
  • enum は公開するとそのヴァリアントはすべて公開される
mod back_of_house {
    pub struct Breakfast {
        pub toast: String,
        seasonal_fruit: String, // このフィールドにはアクセスできない
    }

    pub enum Appetizer {
        Soup,
        Salad,
    }
}

pub fn eat_at_restaurant() {
    let meal = back_of_house::Breakfast {
        toast: String::from("Rye"),
        seasonal_fruit: String::from("blueberries"), // error
    };

    let order1 = back_of_house::Appetizer::Soup;
    let order2 = back_of_house::Appetizer::Salad;
}

use キーワード

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    // before:
    // crate:front_of_house::hosting::add_to_waitlist();

    hosting::add_to_waitlist();
}


  • 慣例的に、
    • 関数をuseで持ち込む場合、親モジュールをuseしてsome_module::func()と書く
      • パスを省略しつつ関数がローカルで定義されていないことを明らかにできるため
    • 構造体やenumuseで持ち込む場合、フルパスを書く
  • as で別名をつけることもできる。モジュール間で名前の衝突を回避するときなどに使える
use std::fmt::Result;
use std::io::Result as IoResult;


  • pub use を使うと名前を再公開(re-exporting)できる
    • 下の例では、このライブラリクレートを使う外部のコードも hosting::add_to_waitlist() を使えるようになる
// src/lib.rs
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

pub use crate::front_of_house::hosting;

モジュールを複数のファイルに分割する

https://doc.rust-jp.rs/book-ja/ch07-05-separating-modules-into-different-files.html

  • src/lib.rsmod xxx; という宣言だけ書くと、モジュールの中身は src/xxx.rs というファイルを見に行くようになる
// src/lib.rs
mod front_of_house;

pub use crate::front_of_house::hosting;
// src/front_of_house.rs
pub mod hosting {
    pub fn add_to_waitlist() {}
}


  • さらに、 src/xxx.rs 内で mod yyy; という宣言だけ書くと、モジュールの中身は src/xxx/yyy.rs というファイルを見に行くようになる
// src/lib.rs
mod front_of_house;

pub use crate::front_of_house::hosting;
// src/front_of_house.rs
pub mod hosting;
// src/front_of_house/hosting.rs
pub fn add_to_waitlist() {}

というわけで、ディレクトリ構成は以下のようになりそう。

- src/
  - lib.rs
  - module_a.rs # サブモジュール含む
  - module_a/
    - sub_module_a.rs
  - module_b.rs # サブモジュール含まない
  - ...
  • モジュールツリーとしては変わらないので、使う側の use 宣言や呼び出し方は何も変わらない

lib.rs で定義したモジュールを main.rs で使うには?

ドキュメントでは触れられていなかった部分。パスをパッケージ名から始める必要がある。

// Cargo.toml
[package]
name = "restaurant"
version = "0.1.0"
// src/main.rs
use restaurant::hosting;

fn main() {
    hosting::add_to_waitlist();
}

こうすると使えるようになるが、根拠となるドキュメントは見つけられてない。
Stackoverflow のこれは若干関係しそう。

Rust modules confusion when there is main.rs and lib.rs - Stack Overflow

余談: mod.rs

「Rust モジュールシステム」とかでググると、モジュールを xxx/mod.rs というファイルで定義する方法が紹介されてる記事が見つかるが、

Rustのモジュールの使い方 2018 Edition版 | κeenのHappy Hacκing Blog

を読む限り、これは 2015 Edition の頃の後方互換性のために用意されているらしい。

これ以上深追いはせず、素直に公式ドキュメントで案内されてる方法を使うことにする。

@octokit/rest.jsのテスト用モックサーバー(@octokit/fixtures-server)のしくみ

@octokit/rest.jsGitHub 社が提供する公式の REST API クライアントライブラリです。
以前この@octokit/rest.js のアーキテクチャがどうなっているか調査した 際、テストに @octokit/fixtures-server という別のパッケージが使われていることまではわかったものの、こいつが何をやっているのかまでは調べきれなかったので、今回はそのあたりを調査したメモです。

なお、 @octokit/rest.js は JavaScript 向けのクライアントライブラリですが、今回調べたモックサーバーについては特定の言語に依存せず利用できます。

  • 調査時のパッケージバージョン
  • 先にまとめ
  • 登場するパッケージとその役割
    • @octokit/fixtures
    • @octokit/fixtures-server
    • @octokit/rest.js
  • fixture ファイルを生成・管理するしくみ
    • 1. 各 scenario ディレクトリ下の record.js に従い、GitHubREST API を実行する
    • 2. レスポンスデータの正規化(normalization)を行う
    • 3. 2の結果と、すでに保存済みの normalized-fixture.json と比較し、差分があれば出力する
    • 保存済みの fixture ファイルと実際の REST API レスポンスに乖離がないか、定期的にチェックする
  • おまけ: bin/record.js を自分で実行してみる
    • 2. アクセストークンを発行し、環境変数にセットする
    • 3. 新しい scenario ディレクトリを作成し、record.js を定義する
    • 4. bin/record.js を実行する
    • 5. bin/record.js を --update オプションつきで実行する
    • 6. リポジトリの内容を編集し、再度 bin/record.js を実行する
続きを読む

2020年7〜9月のふりかえり

10月も間もなく終わろうかというところですが、2020年第3四半期(7〜9月)にやっていたことのふりかえり。

過去のふりかえり

2020年7〜9月にやったこと

7月:「実践Rust入門」を読んでRustに入門した

5月から読み始めて7月末にようやく読み終えた。
感想はこちらに:「実践Rust入門」を読んだ - dackdive's blog

読み終えた当初は「んーまだ自分で書けるようになった感じは全くしないけどとりあえず読めるようにはなったかな」という感触だったんだけど
9, 10月あたりRustを書く機会を全く作れず、今だと読む方も怪しくなってしまった。

8月:WebAssembly に入門した

Rust が終わった後、WebAssembly がどういうしくみで動作しているかぐらいは理解しておきたいなと思い Rust x WebAssembly の入門寄りの記事をいくつかこなした。

最初 Tour of WebAssembly をやったけどさっぱりわからなくて日本語の良さそうな本を読み、それでも結局よくわからないので公式ドキュメントに戻ってきた感じ。
ふりかえると最初から Rust and WebAssembly やっておけばよかったなという感想。

「wasm-bindgen とか wasm-pack ってなんなの?」とか「今 Rust で WebAssembly 書くならどういうツール導入して開発するの?」みたいなものはぼんやり理解した。
ただ学んだことをアウトプットできてないので人に説明できるレベルではない。次の四半期のうちにまとめたい。

その他学習リソースになりそうなもののまとめ:Rust, WebAssemblyの学習に良さそうなチュートリアル系記事まとめ - dackdive's blog

9月:AWS CDK に入門した

7月末ぐらいに東大のAWSの講義資料がバズってて、自分もAWSの知識が全然なくて勉強したいなと思っていたのでやってみた。

東京大学の講義「AWSによるクラウド入門」をTypeScriptで写経した - dackdive's blog

講義内容はCDKに限定せずAWSのさまざまなサービスを使いながら今どきのクラウドについて学ぼうという趣旨のものだったんだけど、
自分にとってはCDKでAWSのリソースを管理するというのがどういう感じなのか掴めてよかった。

9月〜10月: OAuth 2.0 を学び直している(WIP)

業務でのちょっとした出来事がきっかけで、自分がOAuth 2.0についてなんとなくしか理解できてないことに気づき、
基本的なところからおさらいしている。

手始めにAuthlete社のこの動画を観て、

www.youtube.com

次に

【電子版】雰囲気でOAuth2.0を使っているエンジニアがOAuth2.0を整理して、手を動かしながら学べる本 - Auth屋 - BOOTH

を読み、現在は

を読んでいる最中。

自分は手を動かして勉強した方が理解が深まるタイプなので、Rust 書けてない問題も合わせて自分で簡単な認可サーバーを作るのを次の目標としても面白いかなと思っている。

leetcode

前回ふりかえり時点で6問。いま11問。

f:id:dackdive:20201026090122p:plain

8月後半ぐらいから全然やれなかった。

所感

この3ヶ月どうだったか、今のお気持ちは、など。

本を買う/読むハードルが低くなった

特に今やっているOAuthの勉強をしてて感じたこと。
これまでは本を買って読む心理的なハードルが高かったというか、「買った本ははじめから終わりまで丁寧に読み進めて全部理解しないともったいない」みたいな(ケチくさい)考え方だったんだけど
一冊読んで解決できなかった疑問を解消するためにまた別の本を買って、気になるトピックだけかいつまんで読むというのも効率が良いやり方だと感じた。

いま読んでる OAuth 徹底入門 がそんな感じ。

  • 読む前に、何がわかってないのかリストアップしておく
  • 目次を読んで「ここにこういうことが書いてそう」みたいなあたりをつける
  • そこだけ読む

というのにトライしている。

ネットワーク周りの勉強したい欲が高まった

この四半期に
Content delivery networks (CDNs)
という記事を読んで、あと他のきっかけもあり、CDNの基本的なしくみとかSSL/TLSとか、ネットワークまわりの知識が皆無なので勉強したいなーと思うようになった。

OAuthの勉強の傍ら、先日こちらの本を読んで

非常にわかりやすかったので、もうちょっと理論的な話とか勉強したいなと感じている。

次の四半期(2020年10〜12月)のテーマ

メインはOAuthになりそう。

  • OAuth 徹底入門 を読む
  • Rust で OAuth 認可サーバーを作る
  • Rust x WebAssembly で学んだことを整理し直す

年末に立てたTryの確認

東京大学の講義「AWSによるクラウド入門」をTypeScriptで写経した

AWSによるクラウド入門

少し前に話題になっていた東京大学の講義資料をやってみたので、内容、感想などメモ。

講義で使用するソースコードはすべて Python で書かれていますが、自分が実際に使うとしたら TypeScript で書くだろうなと思ったので TypeScript で写経しました。
が、CDK のコードはすべて TypeScript で書けましたが、Lambda 関数や動作確認用のスクリプトなどを全て置き換えるところまでは至らず、Python のままです。

写経したリポジトリhttps://github.com/zaki-yama-labs/intro-aws に。

続きを読む

HTMLのフォームコントロールをカスタマイズ可能にするプロポーザル(Enabling Custom Control UI)

Edge の PM (Program Manager) やってる方のツイートが目に留まったので内容をざっくり読んでみた、という話です。

該当のプロポーザルが置かれているリポジトリ https://github.com/MicrosoftEdge/MSEdgeExplainers から、Edge チームが中心になって検討を進めているプロポーザルのようですが、冒頭の Authors を見る限り他にも Google, Salesforce の人が関わってます。

※以下、 https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/ControlUICustomization/explainer.md の内容をかいつまんで説明したものであり、また追加で調べたことなど自由に補足を入れているため、完全な翻訳ではありません。

Introduction

  • ブラウザに組み込まれたフォームコントロール<select> やさまざまなタイプの <input>。以下「コントロール」)の問題点として、開発者がサイトのデザインやユーザー体験にフィットするように見た目を柔軟にカスタマイズできないことが挙げられる
  • Survey results for controls & components | Greg Whitworth というサーベイによると、開発者がコントロールをリライトする一番の理由は、ネイティブのコントロールの見た目を十分にカスタマイズすることができないことだったそう
    • リンク先のこのグラフがそれを表してそう

f:id:dackdive:20200817030539p:plain
http://gwhitworth.com/surveys/controls-components/ より引用

  • 開発者がスクラッチでコントロールを再実装すると、web プラットフォームがやってくれているパフォーマンス、信頼性(reliability)、アクセシビリティの最適化の恩恵を受けられない
    • 注: "they're not able to leverage the work done on the Web Platform to optimize performance, ..." なので、「プラットフォームの取り組みを活用してパフォーマンス、... を最適化することができない」と訳した方が適切?
  • このプロポーザルによって、開発者はプラットフォームの恩恵を受けつつ、自分たちのサイトにフィットするようネイティブコントロールをカスタマイズすることが可能になる

Goals

  • 開発者がネイティブコントロールの任意の箇所をスタイリングできる
  • 開発者がネイティブコントロールの好きな部分に任意のコンテンツを挿入できる
  • 複数のパーツからなるコントロールに対し、開発者はすべてのUIをリライトすることなく特定の箇所をスタイリングできる
  • 開発者はデータモデルやユーザー入力にリーチするためのコードを再実装することなく、UIをカスタマイズできる。これはカスタムエレメントとしてスクラッチ実装するような今のアプローチとは対照的である
  • カスタマイズしたコントロールはデフォルトでアクセシブルである

Incremental Approach

  • このドキュメントの目的は上述したゴールを達成でき、かつすべてのコントールに適用できるようなモデルを検討することだが、実際にはモデルの適用はそれぞれのコントロールずつ - つまり各コントロールごとに固有のプロポーザル - になるだろう
    • ロールアウトは開発者の要求(demand)や各コントロールの複雑度合によるし、一部のコントロールには適用されない(実装されない)可能性もある
  • このドキュメントはコンセプトの例として <select><input type="range"> を多用するが、これらのコントロールの詳細なスペックを決めることがゴールではない
    • 全体的なアプローチについて合意がとれたら、これらや他のコントロールの固有なふるまいについて詳細を詰めていく
    • 進捗はすでに https://open-ui.org/ の方で確認できる

Use Cases

  • 主要なユースケースは「コントロールを自分のサイトに配置したいが、コントロールの見た目に高いレベルでの柔軟性を必要としているWeb開発者」
  • 現在、そのようなシチュエーションでは、コントロールのscriptable model(?)とUIをリンクするコードや、アクセシブルにするためのARIA属性などを含めたコードをスクラッチで実装する必要がある
    • このプロポーザルにより開発者はカスタムのマークアップとスタイルのみ提供し、残りはプラットフォームが提供する(というように役割分担がされ、開発者は見た目のカスタマイズに注力できるようになる)

Proposed Solution

Custom content and styles via standardized parts, named slots, and shadow DOM replacement

開発者がネイティブコントロールをカスタマイズする方法には3つの選択肢がある:

  1. 標準化されたパーツと状態(standardized parts and states)を使い、擬似クラスと擬似要素でネイティブコントロールのスタイルを上書きする
    • 擬似クラス: :hover:first など (MDN)
    • 擬似要素: ::after など (MDN)
  2. 名前つき <slots> を使い、ネイティブコントロールの一部はそのままに、一部のパーツを開発者が作成したコンポーネントに置き換える
  3. ネイティブコントロールのUI全体を開発者が提供する shadow root で置き換える

下にいくほど自由度が高くなるイメージ。

Standardized control UI anatomy, parts, and behavior

上のいずれの方法を取るにしても、今のコントロールの概念的なパーツやふるまい、ならびに各パーツの名称についての標準化が必要。
このプロセスは現在 https://open-ui.org/ によって進められている。

標準化の例として、たとえば <select> については

  • 以下のパーツで構成される
    • 1つの "button" という名前のパーツ
    • 1つの "selected-value" という名前のパーツ
    • 1つの "listbox" という名前のポップアップパーツ
    • 0〜N 個の "option" という名前のパーツ
  • 期待するふるまいは以下の通り
    • "button" がクリックされるとリストを表示する
    • "selected-value" は内部テキストを更新し現在選択中のオプションの値を表示する
    • 開いているリストの外をクリックすると折りたたむ
    • etc.

開発者はここで定義されたパーツからなるカスタムUIを提供すれば、プラットフォームがよしなにイベントハンドラーやARIA属性を付与して期待するふるまいやアクセシビリティを担保してくれる、というしくみ。

なおネイティブのスタイルは標準化される予定がなく、ブラウザによって今後も差異がある予定。

選択肢1のアプローチ(Styling native parts using pseudo-classes and the part pseudo-element)

先ほどの標準化によって決定された各パーツの名称と、 part という擬似要素を使って、以下のように今より細かい粒度でスタイルを適用できる。

<style>
  .styled-select::part(button) {
    background-color: red
  }
</style>
<select class="styled-select">
  <option>choice 1</option>
  <option>choice 2</option>
</select>

選択肢2のアプローチ(Named slots)

標準化によって決定された各パーツの名称に対応するように slot の name 属性も標準化することで、開発者はカスタマイズしたいパーツを slot name で指定して任意のコンポーネントに置き換えられるようになる。

たとえば <select> であれば "button" や "listbox" という名前が標準化されるので、

<style>
  .custom-button {
    /*...*/ 
  }

  option {
    /*...*/
  }

  .option-text {
    /*...*/
  }
</style>
<select>
  <div slot="button" part="button" class="custom-button">Choose a pet</div>
  <div slot="listbox" part="listbox" class="custom-listbox">
    <option>
      <img src="./cat-icon.jpg"/>
      <div class="option-text">Cat</div>
    </option>
    <option>
      <img src="./dog-icon.jpg"/>
      <div class="option-text">Dog</div>
    </option>
  </div>
</select>

こういったコードを書くことで、↓のようなドロップダウンが作れる。

f:id:dackdive:20200819005527p:plain
https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/ControlUICustomization/explainer.md より引用

コンテンツを与えなかった slot についてはデフォルトのUIにフォールバックされる。

選択肢3のアプローチ(Shadow DOM replacement)

注:3つの選択肢のうち、これだけ親となるセクションが異なっていて、文章のつながりにいまいち自信が持てなかった。けど、おそらく上述した選択肢の3番目の説明にあたると思われる。

attachShadow() を呼ぶことで、UIをまるごと開発者が用意したコンポーネントで置き換えちゃうアプローチ。

let customSelect = document.createElement('select');
customSelect.setAttribute("custom", "");
let selectShadow = customSelect.attachShadow({ mode: 'open' });
selectShadow.innerHTML = `My custom select UI`;
document.body.appendChild(customSelect);

今はコントロールに対して attachShadow() を実行すると例外を throw するが、カスタマイズができるようになるとこのメソッドが実行可能になる。

この場合も、プラットフォーム側は開発者が渡した shadow DOM にコアとなるパーツ(<select> なら part="button"part="listbox")が含まれているかどうかチェックする。

もし必須のパーツが渡されなかった場合は、shadow DOMはレンダーされない。


その他

ここまでで背景やどういう解決方法になりそうなのかはなんとなく把握できましたが、元のドキュメントの内容的には半分ぐらいです。
残りはざっと読んでいくつか気になった部分をメモします。

Light-DOM content under <input>

先ほどの選択肢2のようにカスタマイズしたい内容をコントロールの子要素として配置していくアプローチの問題点として、 <input> には子要素を配置できないという問題がある。

この問題に対するワークアラウンドとして、 <input> の各 type に対応する新しい HTMLElement を標準化することを提案している。
新しい element は対応する <input> element と等価だが、その type と無関係なメソッドやプロパティは取り除かれたものになる。 例: <input type="range"> に対して HTMLRangeElement を新しく導入する。これは <input type="range"> と同じメソッド、プロパティ、ふるまいを持つが、その他の input type にのみ関係するメソッド・プロパティは除外されている。この element は以下のように使うことができる。

<range>
  <div slot="thumb" part="thumb"><svg><!-- Use SVG to draw the thumb icon... --></svg></div>
</range>

Ensuring accessibility by default

このプロポーザルの key goal に「カスタムコントロールはデフォルトでアクセシブルである」というのがあった。
これを達成するために、part 属性の値に応じて ARIA 属性などをプラットフォーム(ブラウザー)側が暗黙的に適用する。

プロポーザル中では implicit accessibility semantics という用語を使っており、これは HTML AAM というドキュメントに定義されている implicit semantics と同じ意味合いだそう。(該当のドキュメントを読んでいないので詳しいことはわかりません)

ARIA 属性は HTML に実際に追加されるわけではないが、プラットフォームは適切な ARIA 属性が付与されているかのようにみなしてアクセシビリティツールに伝える。

たとえば、先ほどの

<range>
  <div slot="thumb" part="thumb"><svg><!-- Use SVG to draw the thumb icon... --></svg></div>
</range>

の場合、プラットフォームは <div part="thumb"> について以下のような implicit accessibility semantics をアクセシビリティツールに伝える。

  • slider という ARIA role
  • aria-valuenow
  • aria-valuemax
  • aria-valuemin

Feature detection

ブラウザ側がこの機能に対応しているかどうかを判定する方法について。

新しい HTMLElement が導入されている要素(先ほどの、<input type="range"> に対する <range> のように)であれば、以下のようにその要素が存在するかどうかでチェックできる。

if (!window.hasOwnProperty("HTMLRangeElement")) {
  /* apply polyfill/fallback */
}

そうでない場合、 attachShadow() が例外を投げるかどうかで判断できる。

function hasCustomSelectFeature() {
  try {
    document.createElement("select").attachShadow({mode: "open"});
    return true;
  } catch (e) {
    return false;
  }
}

Privacy and Security Considerations > Security

コントロールの中には、サードパーティのコードにそのまま提供されていると危険な、特権的な振る舞いを持つものがある。

  • たとえば <select> のドロップダウンリストはブラウザのウィンドウや iframe の外に飛び出すことが可能

f:id:dackdive:20200819014740p:plain:w480

  • このプロポーザルによって任意のコンテンツを <select> に渡せるようになると、OSのUIになりすましたり、iframeから外のコンテンツになりすますといったことが可能
  • すべてのコントロールタイプは、このような潜在的に悪用される可能性のある特権的な振る舞いのケースを慎重に調査する必要がある

「実践Rust入門」を読んだ

一旦読み終わったので、感想や個人的に良かったなあと思ったポイントなどをメモしときます。

全体的な感想

私のように、初めて Rust を勉強する人が基本的な文法やテスト・パッケージ(クレート)などのエコシステムについて体系的に学ぶのに良い本だと思いました。
私の場合、最初は the book と呼ばれる公式ドキュメントから入ろうとしたものの英語に挫折し、日本語訳を見つけるも情報の鮮度にいまいち確証が持てず(エディションがなんなのかいまいちわからなかった)。。。という感じだったので
2018 Edition に対応しており、かつ日本語で書かれた書籍という点では非常にありがたかったです。

また、サンプルも豊富です。

  • 「2-4節 RPN計算機プログラムとデバッガによる実行」では RPN逆ポーランド記法)で記述された数式を計算するプログラム
  • 「第3章 クイックツアー」ではソートアルゴリズムの 1 つであるバイトニックソート
  • 「7-9節 ライフタイムの詳細:簡単なベクタの実装」では簡易的なベクタ型
  • 「第9章 パーサを作る」では四則演算を行うためのパーサ
  • 「第10章 パッケージを作る」ではファイルの文字数をカウントするパッケージ
  • 「第11章 Web アプリケーション、データベース接続」ではWebアプリ
  • 「第12章 FFI」ではRustとCの連携プログラム

というように、実際に手を動かしてある程度のまとまりのあるコードを書くための題材が複数用意されています。
基本文法を覚えた後でいくつか実践的なお題に取り組めるのは良いと思いました。

加えて、本書は500ページ超と自分にとってはかなりのボリュームだったんですが、各サンプルは独立性が高いので、
特に後半の第10章〜第12章は自分に興味のあるトピックだけかいつまんで読み進めることができます。
私も第11, 12章は必要になったときに熟読しようと思い流し見だけしました。

困ったところがあるとすると、RPN計算機、バイトニックソートなど、題材自体の理解が難しいものがいくつかあったことでしょうか。
私は本を読むときたいてい前の方からじっくり読んでしまうんですが、文法もよくわかってない状態であれば第2, 3章はとばして第4, 5, 6, 7章あたりから読めばよかったなーと思いました。

写経したソースコード

https://github.com/zaki-yama-labs/rust-study/tree/master/bicycle-book
にあります。

良かったところ、勉強になったところ

VSCode でのデバッグ方法を知ることができた

「2-4-2 デバッガのセットアップ」の項で、CodeLLDB というVSCode拡張を使ったデバッグ方法について説明されており、これは今後も役立ちそうです。

データがメモリ上にどう配置されるのか、図を交えて説明されておりわかりやすかった

本書でRustを勉強して印象に残ったことの1つとして
「Rustでもデータがメモリ上にどういうふうに格納されているのかとか、ポインタに近い概念みたいなものは意識しないといけないんだ」
という感想を抱いたのですが、本書ではそのあたりの説明が丁寧でわかりやすかったように思えました。

「5-1 スタック領域とヒープ領域」でそれぞれのメモリ領域の特徴の紹介がされていたり、ちょっと話が複雑な場面では「今メモリ上はこのようになっています」というのを説明した図が何度も登場しました。

例として、Rustで文字列を扱う際に登場するStringとstrを比較した図を引用します。

f:id:dackdive:20200729013932p:plain

let foo = "abc" などとしたときの文字列リテラルは不変なので静的領域に置かれるがStringは可変なのでヒープ領域に置かれる、とか、
&str は実データを所有せず参照している、というのが図によってイメージしやすかったです。

パッケージの作り方、テストの書き方が理解できた

これは主に第10章です。
普段はWebフロントエンドの開発をしているため、npm パッケージに相当するものはRustだとなんだろう?とか、テストはどういうライブラリを使うんだろう?といった疑問がこちらの章で解決しました。

また、CI についても触れられていて、実際にRustで何か作ろうとするときには役立ちそうな知識だなーと思いました。
本書で紹介されていたのはTravisCIとAppVeyorでしたが、私はGitHub Actionsで構築しました。

そのへんの話はこちらに書きました:

余談ですが、Cargo.toml に書く [badges] というセクションは現在は deprecated のなんですかね。
Remove badges from crate lists and details · Issue #2436 · rust-lang/crates.io

README にこんな感じでバッジを直接埋め込むとよさそう。

[![<YOUR-CRATE-NAME> at crates.io](https://img.shields.io/crates/v/<YOUR-CRATE-NAME>.svg)](https://crates.io/crates/<YOUR-CRATE-NAME>)
[![<YOUR-CRATE-NAME> at docs.rs](https://docs.rs/<YOUR-CRATE-NAME>/badge.svg)](https://docs.rs/<YOUR-CRATE-NAME>)

実際に試したやつ:https://crates.io/crates/zaki-yama-bicycle-book-wordcount

また、クレート名はグローバルで一意じゃないといけない(npmパッケージのscopeみたいなものがない)、というのも、クレートを公開しようとしたときに地味にハマったポイントでした。

疑問点をSlackで質問できる

https://github.com/ghmagazine/rustbook#ご質問や不具合報告など

にも記載がありますが、本書に関する質問は rust-jp チームの Slack で著者らに質問することができます。
私も2つほど質問させていただきました。とても助かりました。

f:id:dackdive:20200729090419p:plain

引き続き勉強が必要だと感じたところ

「Rustならではの型や構文は覚えたので前より読めるようにはなったけど、正直まだまだ自分でRustを書けるレベルには程遠いな〜」というのが率直な感想です。

具体的には

  • Box<T> 型はまだよくわかってない
  • 所有権システム
    • 参照とか借用とかムーブとか
    • 7-9 節で簡易ベクタ型を実装するとき、メソッドの引数が &self だったり &mut self だったり self だったり。どういうときにどう書くのがいいか全然わからない
    • ライフタイム...
  • ベクタを扱う操作
  • モジュール単位、ディレクトリ構成など
    • 本書では単一ファイルに全部の関数やそのテストも書く、みたいな感じだったので、どういう粒度でファイル分けるんだろうなとか

この記事にもライフタイムは鬼門だって書いてますね。

次やること

次は WebAssembly の作り方、動かし方を勉強したいです。
Tour of WebAssembly をはじめました。

学習メモ

本を読みながらとっていたメモはリポジトリに。
https://github.com/zaki-yama-labs/rust-study/tree/master/bicycle-book