dackdive's blog

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

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

前回:2021年4〜6月のふりかえり - dackdive's blog

✨ やったこと

7月: The Art of WebAssembly を読んだ

6 月から読み始めて、感想ブログを書いたのが 8/2。約2ヶ月かかった。

感想

dackdive.hateblo.jp

8月: Next.js, SWR, Chakra UI などを勉強した

8月は読書を一旦やめて、最近あまりキャッチアップできてないと感じていたフロントエンド周りの技術を勉強した。
結果としてこのようなものを作った。

9月: University of the People (UoPeople) に入学した

前四半期で申し込みまで済ませていた UoPeople だが、9月から本格的に講義がスタートした。
正確にはまだ正式な学生にはなれてなくて、Degree Student と呼ばれる正式な学生になるために必須の科目を受講している。

今は UNIV 1001 Online Education Strategies という一般教養みたいな科目と CS 1101 Programming Fundamentals (Python) というプログラミング基礎みたいな科目の2コマをやっている。すでに限界。

📝ブログ

↑に挙げたものがすべて。

💬 所感

気持ちの部分ではあんまり書くことがない。良い意味では前四半期より気持ちが前向きになっているということなのかもしれないし、悪い意味ではこの四半期大したことしてなくてほぼ記憶がないということなのかもしれない。

それでも、8月にキャッチアップしたかった技術を学べたし9月入ってからは大学の講義についていくので精一杯で、充実感はある。

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

次の四半期でやりたいこと、というのがあんまり思いつかない。。。

  • まずは UoPeople で受講中の2コマちゃんと単位とって正式な学生になりたい
  • 本だと📕 Rust in Action かな

💸 買ったもの

本ぐらい。

Pixela APIをブラウザ上で試せるPlaygroundをNext.jsで作った

作った。
f:id:dackdive:20210905015031p:plain:w480

最低限の機能しかないしコードも汚いところいっぱいあるけどとりあえず公開することにした。
ここで試せます。

pixela-api-playground.zaki-yama.dev

ソースコードはこちらに。

モチベーション

ここのところ仕事では SDK など GUI を持たないライブラリやCLI を作ることが多く、Next.jsswr といった最近よく聞くフロントエンド技術スタックを素振りできずにいて危機感があった。

これらの素振りをするのにちょうどよい題材はないものかと思っていたところ、 Pixela という API サービスに出会ったので
この API をブラウザ上で実行できる簡単な Web アプリケーションを作ってみた。

Pixela について、詳しくは公式サイト(https://pixe.la/ja) の説明に譲るとして、GitHub の Contribution Graph のような草を生やすことができる API サービス。
習慣化したい活動を何でも記録して草を生やすことができる。最高。

自分の場合は、日々の読書時間を記録して GitHub ぽく見れたらなーと思っていたところこのサービスを見つけた。

Pixela は、 すべての機能が REST 風の API でしか提供されていない ところが特徴的。つまり、最初のユーザー登録から何からすべて curl などを利用して直接 API を叩いて行う必要がある。

これは API を利用することに慣れたプログラマにとって非常にとっつきやすい一方、特に最初どんな API があるのか、どんなリクエストに対してどんなレスポンスが返ってくるのかを色々試したいようなときには GUI でのインターフェースもあると自分以外の人にとっても便利かもなーと思い、ブラウザ上で API を一通り試せるようなサイトを作ることにした。

機能について

今のところは本当に各種 API を揃えただけ、という感じなので、便利機能とかはないです。
グラフ表示 API など一部の APIJSON ではなく SVG でレスポンスが返ってくるが、そういったものはサイト上で直接画像が見られるようになっている。

f:id:dackdive:20210905003457p:plain:w480

今後時間があれば作りたい機能はいくつかあって、

  • ログイン機能
    • 自分が使う上で一番ほしい。username, token を毎回入力したくない
  • レスポンスの JSON をコピーするボタン
  • ユーザーのグラフをまとめて表示するページ
    • どちらかというとダッシュボードでは感はあるが
  • サポーター限定の機能(パラメータ)はそれとわかるようにしたい
  • ダークモード対応
    • 技術的にやってみたいというだけ

などなど。

反応があればやる気も上がると思うので、リポジトリに Star をお願いします...🙏

技術的な話

今回使った主な技術スタックは以下。

それぞれ、初めて使ってみた感想とかよくわからなかったとこを書く。

Next.js

公式ドキュメントが非常に充実しているので、まずは Learn Next.js を一通りやってその後は必要になった機能だけ個別に調べる、というやり方で十分だった。

今回は必要にならなかったけど SSG、SSR、ISR の違いとかが理解できた。

SWR

今回のようにボタン押したらリクエスト送るだけのアプリに対しては完全に too much だったとは思うものの、どういう使い勝手なのか試したくて導入した。

条件付きフェッチ – SWR
の項にもあるように、「ボタンが押されたら useSWR() する」のではなく「レンダリングのたびに常に useSWR() は実行されるので、実際に fetch するかどうか別の state で管理する」というところは
ちょっとこれまでと発想を変える必要があった。けど hooks に共通して言える考え方なんだろうな、そういう意味で hooks 時代の設計に頭が追いついてないんだろうなとも思った。

あと、恥ずかしながらしばらく fetch や axios などのライブラリの代わりとして選択できるものだと思っていて、あくまでデータの「取得」用のライブラリだということに遅れて気がついた。
ref. how to use post method and pass params in swr? · Issue #93 · vercel/swr

🤔 わからなかったこと

クエリパラメータがそこそこあるような API を叩くとき、 useSWR()key パラメータをどう指定するのがお作法的に正しいのかよくわからなかった。

引数 – SWR にあるように第一引数の key には配列を渡せるが、

const { data: user } = useSWR(['/api/user', token], fetcher)

クエリパラメータが複数あるからといってそれをオブジェクトにまとめてしまうのは NG とされている。

// NG
const { data: events } = useSWR(['/api/events', { from, to }], fetcher)

基本的にクエリパラメータが異なると別々の結果としてキャッシュしたいから key には含めておきたいと思うんだけど、
クエリパラメータが増えるごとに配列に追加することになる?

そうすると fetcher 関数の引数も増えて...って思ったけど、第二引数をこう書けばいいのか。

const { data: events } = useSWR(['/api/events', from, to], (url, from, to) => fetcher(url, { from, to })

Chakra UI

よくある UI コンポーネントライブラリの1つとして必要なものだけ使った、という感じなので、どのあたりに強みがあるのかとかはまだ理解が不十分。

Comparison - Chakra UI
ここを一回ちゃんと読んだほうがいい。

あと、Chakra UI を使えば Tailwind CSS の思想とかも理解できるかと思ったけど全然そんなことはなかった。

あわせて読みたい

React Hook Foom

フォーム項目にきめ細かいバリデーションを実装したい場合に力を発揮しそうだなと思いつつ、今回は必須項目ぐらいしかなかったのでこれも too much ですね。

また、Chakra UI のドキュメントに
Chakra UI + React Hook Form - Chakra UI
なんてページもあるぐらいだから両方組み合わせるのも余裕なんだと思ってたけどそんなことなかったです。
ラジオボタン (Radio, RadioGroup) にどう組み込むのかわからず今のところこうなっている。

<FormControl>
  <FormLabel>type</FormLabel>
  <Controller
    control={control}
    name="type"
    render={({ field: { onChange, value } }) => {
      return (
        <RadioGroup onChange={onChange} value={value}>
          <Stack spacing={4} direction="row">
            <Radio value="int">int</Radio>
            <Radio value="float">float</Radio>
          </Stack>
        </RadioGroup>
      );
    }}
  />
</FormControl>

🤔 わからなかったこと

「単純なラベルつき input + 必須バリデーション + エラー時のメッセージ表示」みたいなことをやろうとすると
こういうコードを何回も書くことになる。

<FormControl isInvalid={!!errors.username}>
  <FormLabel htmlFor="username">username</FormLabel>
  <Input
    id="username"
    type="text"
    {...register("username", {
      required: "This is required.",
    })}
  />
  <FormErrorMessage>
    {errors.username && errors.username.message}
  </FormErrorMessage>
</FormControl>

さすがにこれはめんどいってことでコンポーネントに切り出したけど、TypeScript の型が合わず何箇所か挫折してる。

type Props<TFieldValues extends Record<string, any>> = {
  name: keyof TFieldValues;
  required?: boolean;
  register: UseFormRegister<TFieldValues>;
  errors: FieldErrors<TFieldValues>;
};

export default function Input<TFieldValues>({
  name,
  required,
  register,
  errors,
}: Props<TFieldValues>) {
  return (
    <FormControl isInvalid={!!errors[name]}>
      {/* @ts-ignore */}
      <FormLabel htmlFor={name}>{name}</FormLabel>
      <ChakraUIInput
        // @ts-ignore
        id={name}
        type="text"
        // @ts-ignore
        {...register(name, {
          required: required && "This is required.",
        })}
      />
      {/* @ts-ignore */}
      <FormErrorMessage>{errors[name]?.message}</FormErrorMessage>
    </FormControl>
  );
}

これは正解がよくわからない。

「The Art of WebAssembly」を読んだ

読んだので簡単なまとめを。

この本について

前半は WebAssembly の基本的なしくみや Node.js・ブラウザで動かす方法、後半はパフォーマンス測定やデバッグ方法について学べる本。
また、特定の言語やツールチェイン(たとえば Rust だったら wasm-pack とか)に依存した内容ではなく、WAT と呼ばれる WebAssembly のテキスト表現を書きながら実装する。

構成

ざっくり Chapter ごとにやってることを書く。

  • Chapter 1: Introduction to WebAssembly
    • WebAssemblyとは何か、その特徴は、なぜ WebAssembly を使うかみたいな話
    • WebAssembly Text Format (WAT) の紹介もここで
  • Chapter 2: WAT Basics
    • WAT の基本構文:関数の import、data、変数、if 文や loop など
  • Chapter 3: Functions and Tables
    • WebAssembly で定義した関数を JS から呼び出す
    • Table (function table) について。Table を使った場合と関数を直接呼び出した場合のパフォーマンス比較
  • Chapter 4: Low Level Bit Manipulation
    • ビット演算について。 読んでない
  • Chapter 5: String Manipulation
    • 文字列操作
    • Null-Terminated Strings と Length-Prefixed Strings という 2 つのポピュラーな方法
  • Chapter 6: Linear Memory
    • linear memory の使い方
    • オブジェクトのような複雑なデータ構造を linear memory でどう管理するか
  • Chapter 7: Web Applications
    • ブラウザで WebAssembly を実行する方法
    • DOM 操作をどう実現するか
  • Chapter 8: Working with the Canvas
    • Canvas を使ったアニメーション
  • Chapter 9: Optimizing Performance
    • ブラウザの DevTools を使ったパフォーマンス計測と改善
    • パフォーマンス改善につながるテクニック
  • Chapter 10: Debugging WebAssembly
  • Chapter 11: AssemblyScript

Chapter 8 の Canvas アプリケーションは↓のような、物体の衝突判定アプリ。
正方形の形をした大量の物体が一定速度で動いており、他と衝突してるときだけ色が変わるというもの。
フレームごとの物体の座標計算と衝突判定を WebAssembly 側でやっている。

f:id:dackdive:20210802001349g:plain:w480

これは https://rustwasm.github.io/book/ で作ったものと似てるなーと思った。

全体的な感想

C や Rust からのコンパイルではなく素の WAT をひたすら書くので実用的な知識が得られたとは思わないけど、知らなかったことも多かったので学びはあった。

特に

  • WebAssembly では文字列扱うの難しいのは知っていたが、Chapter 5 で具体的なアプローチとともに文字列操作の方法を学べた
  • Chapter 6 の linear memory の章で、やや複雑な( i32 みたいなプリミティブな値でない)データ構造を扱うために linear memory 上にデータをどう格納するのかがわかった
  • Chapter 9 で DevTools でのパフォーマンスの見方がわかった(これは若干 WebAssembly 関係なくなるけど)
  • ところどころでパフォーマンス比較の話が登場しており、WebAssembly をどういうとき/どういうふうに使うべき/べきでないみたいなのが腹落ちした。たとえば:
    • 純粋な WebAssembly 関数と、WebAssembly 関数の内部で JS から import した関数を呼び出した場合
    • Table を使って間接的に関数を呼び出した場合と直接呼び出した場合
    • 同じ処理内容を WebAssembly で実装した場合と JS で実装した場合

というあたりはよかった。

反面、WAT の構文の説明とか動作原理についてきちんと説明されてないと感じる部分もいくつかあった。たとえば i32.store / i32.load とか data とか。
local variable や global variable ってデータ上はどういうふうに格納されてるの?とか。

WAT の構文だけで言えば、 MDN のこのページを読むのとそんなに大差ないのでは、と感じた。

本自体の構成については、300 ページ弱あるもののサンプルコードは基本的に省略せず全部載せているためコードが大半を占めていて、ページ数から受ける印象ほど読むのが辛くなかった。
またコードの説明はかなり丁寧に書かれていた。

なんだかんだ英語の本を読むのが初めての挑戦だったけど挫折せずに完走できてよかった。

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

Chapter 2: WAT Basics

WAT の記法は S式 (S-Expression) 以外に Linear Instruction List Style というのもあり、両者が混在している。

S 式で表現した

(i32.add (i32.const 3) (i32.const 2))

は、Linear Instruction List Style で書いた以下と等価。

i32.const 3
i32.const 2
i32.add

後者は値が上から順にスタックされてく様子を脳内で覚えておかないといけないので、S 式の方が読みやすいと個人的には思う。

Chapter 5: String Manipulation

文字列を linear memory を介して JS とやり取りする場合、文字列の開始位置とともに文字列がどこで終わるのかという情報も JS 側がわかる必要がある。
そのための2つのポピュラーな方法として Null-Terminated Strings と Length-Prefixed Strings という方法がある。

1) Null-Terminated Strings

文字列の終わりに null 終端文字列を挿入する。

;; wat 側
(data (i32.const 0) "null-terminating string\00")
// js 側
const bytes = new Uint8Array(memory.buffer, str_pos, max_mem - str_pos);

let log_string = new TextDecoder("utf8").decode(bytes);
log_string = log_string.split("\0")[0];

2) Length-Prefixed Strings

文字列の最初に文字列自体の長さを埋め込んでおく。

;; wat 側
;; 22 文字で、16 進数にすると 16
(data (i32.const 512) "\16length-prefixed string")
// js 側
const str_len = new Uint8Array(memory.buffer, str_pos, 1)[0];
// 文字列自体の長さが先頭に1バイト埋め込まれているので + 1
const bytes = new Uint8Array(memory.buffer, str_pos + 1, str_len);
const log_string = new TextDecoder("utf8").decode(bytes);
Chapter 6: Linear Memory

Linear Memory は「ページ」という単位で確保する。1ページ 64 KB、最大ページ数は 32,767。

WAT 側では

(memory 1)

JS 側では

const memory = new WebAssembly.Memory({ initial: 1 });

というように初期化時にページ数を指定する。

(p125 「Collision Detection」)
あるデータ構造の配列を linear memory に格納する場合、以下の情報が必要。

  • starting address: linear memory 上でそのデータ構造の配列が始まる位置
  • stride: データ構造1つあたりに必要なバイト数 = distance in bytes between each structure
  • offset of any structure's attributes: データ構造の各属性へのオフセット
    • たとえば x, y という属性があり、それぞれ 4 バイトだとすると、そのデータ構造の属性 y のオフセットは 4

f:id:dackdive:20210802013205p:plain

学習メモ

読みながら取ったメモは
https://github.com/zaki-yama/learn-rust-and-webassembly/tree/main/the-art-of-webassembly
の章ごとのディレクトリに置いてある。

2021年4〜6月のふりかえり

前回:2021年1〜3月のふりかえり - dackdive's blog

✨ やったこと

「ゼロからの OS 自作入門」は中断(挫折)した

3 月に買った OS 自作入門だが、手元のメモによると 5 月上旬ぐらいで更新が止まっている。。。
たしか、6 月に「The Art of WebAssembly」という本が発売されることを知ってそっちに興味持ってからゆるやかに終了してしまったんだな。

The Art of WebAssembly を読んでいる

というわけで 6 月から読み始めたのはこの本。

C++もRustも使わずWATファイルを直接手書きしながらWebAssemblyを学んでいく、というストロングスタイルの本。
長いが今のところわかりやすい。

📝ブログ

この四半期に書いたブログは以下。

また、ブログとして消化できてないが、こういったことを調べてた時期もあった↓

TypeScriptでType AliasesとInterfacesどちらを使うべきか

💬 所感

続:なんとなくしんどい

前回のふりかえりでも「集中力・モチベーションが上がらないと感じる時期があった」って書いてるけど、相変わらず仕事もプライベートもなんとなくモチベが上がらない感覚はある。
これについては少し前のこういった記事を目にした。

これを読んで、そうか自分は languishing なのかもしれないと認識するようになった。また、名前がついているぐらいだから自分以外にも似たような感覚の人は少なくないのかもしれない、ということがわかって少し気が楽になった。

あとここに詳細を書くのは憚られるんだけど、身内の不幸とかもあってそれもけっこう心を揺さぶられる要因になったりもした。

Rust の勉強が止まってしまった

WebAssembly の本を読み始めたのはいいけど、Rust のコードを書く機会がめっきりなくなってしまった。
がんばって Exercism 再開したいなーとも思うし、Twitter で見かけたこの本もセールで衝動買いしてしまったので次読むとしたらこの本かもしれない。

UoPeople (University of the People) に申し込みした

ようやく手続きした。事前にがんばってTOEFLのスコアを取得してたので、最初の英語の授業は免除できそう。
ただ、タームの期限最終日に申し込みしたりTOEFLのスコア提出を後からメールで送ったりした関係で、本来6月からスタートできてたはずが最初のコースは次タームからになりそう。

そして、この間にいろいろあったのでモチベーションは勢いで申し込んだときより下がってしまっている。。。
やってみてだめならやめる、卒業をゴールにするとしんどいので興味あるCS系の授業を自分のペースで学ぶ、ぐらいの気持ちでやっていきたい。

💪次の四半期(2021年7〜9月)のテーマ

  • 📕The Art of WebAssembly
  • 📕Rust in Action

💸 買ったもの

logicool ワイヤレスマウス MX ERGO

★★★★★
同僚氏がオススメしてたので買った。トラックボールは使ったことがなかったけど非常に良い。

ダブルステンレスドリッパー・粕谷モデル

★★★★☆
ほぼ毎日コーヒーを飲むので買った。
以前は安物のセラミックフィルターだったので洗わないといけない金属フィルターには抵抗がなかった。 ネルドリップっぽいかどうかはわからないけどたしかに味はまろやかになった気がする。人によってはちょい物足りないと感じるかも。

GitHubの新しいissue forms機能(ベータ)を試したメモ

こちらのツイートをしたときはまだ一部のリポジトリしか有効になってなかったんだけど、先日ついにすべてのパブリックリポジトリで利用可能になった。(まだベータという位置づけ)

というわけで自分のリポジトリで試したメモ。

ドキュメントはここにある。

https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#creating-issue-forms

issue forms機能とは

GitHub には元々 Issue のテンプレート機能 があったが、テンプレートファイルを Markdown でなく YAML で用意すると issue 作成時にフォームで表示してくれるというもの。

実際の画面キャプチャを載せる。
https://github.com/zaki-yama-labs/github-issue-forms-playground で New issue すると見れる。

f:id:dackdive:20210629235241p:plain:w480

自由記述式のテキスト項目だけでなく、ドロップダウンやチェックボックスも作れる。

設定方法

Markdown のときと同じく、リポジトリ.github/ISSUE_TEMPLATE ディレクトリを作り、その下に YAML ファイルを置く。 YAML ファイルのサンプルを 公式ドキュメント から引用する。

name: Bug Report
description: File a bug report
title: "[Bug]: "
labels: [bug, triage]
assignees:
  - octocat
body:
  - type: markdown
    attributes:
      value: |
        Thanks for taking the time to fill out this bug report!
  - type: input
    id: contact
    attributes:
      label: Contact Details
      description: How can we get in touch with you if we need more info?
      placeholder: ex. email@example.com
    validations:
      required: false
  - type: textarea
    id: what-happened
    attributes:
      label: What happened?
      description: Also tell us, what did you expect to happen?
      placeholder: Tell us what you see!
      value: "A bug happened!"
    validations:
      required: true
  ...

トップレベルの name, description といったプロパティについては
Syntax for issue forms > Top-level syntax を参照。

メインとなるフォーム部分は body 内にフォーム要素の配列として定義する。
フォーム要素のプロパティについては
Syntax for GitHub's form schema を参照。

以下、個人的に気になったポイント。

表示できるフォーム要素のタイプ

現在は以下の5種類。

  • markdown
  • input
  • textarea
  • dropdown
  • checkboxes

markdown は文字通り Markdown で任意のメッセージを記載できるというものだが、これはフォーム上で表示されるだけで submit されない = 登録された issue には記載されない。

- type: markdown
  attributes:
    value: |
      `type: markdown` はフォームに表示されるがsubmitされない(登録されたissueには記載されない)。
      issue 登録者向けのメッセージとして使えそう。

f:id:dackdive:20210630003056p:plain

textarea に render: <言語> を指定するとコードブロックになる

textarea には render というプロパティがある(ドキュメント)。
render: javascript などのようにプログラミング言語を指定すると、結果がコードブロックとして表示され、指定した言語でシンタックスハイライトされるようになる。

- type: textarea
  id: textarea-2
  attributes:
    label: "Textarea (render: javascript)"
    description: "render: <言語> を指定するとコードブロックになり、指定した言語でシンタックスハイライトができる"
    render: javascript

f:id:dackdive:20210630004028p:plain

id はURLパラメータで値をセットするために使える

すべての type に存在する id というパラメータ。
何に使うのかと思ったけど、ドキュメントによれば

If provided, the id is the canonical identifier for the field in URL query parameter prefills.

とあるので、指定しておくとそれをURLパラメータで使うことで値をセットできる。

たとえば
https://github.com/zaki-yama-labs/github-issue-forms-playground/issues/new?assignees=zaki-yama&labels=&template=sample.yml&title=%E3%82%BF%E3%82%A4%E3%83%88%E3%83%AB%3A+&input-1=foo,%20bar
こういうURLだと最初の input 要素に foo, bar という文字列を初期値としてセットできる。

トラブルシューティング

構文エラーになってた場合、該当ファイルを開くと上部にエラーが表示される。

f:id:dackdive:20210630005030p:plain

各エラーメッセージの「Learn more」をクリックすると詳細に飛べる。

所感

ドロップダウンやコードブロックや画像添付、 必須(required)オプションなど、ベータだけどだいたい必要なものは揃ってる感じ。 従来の Markdown にコメントで案内書くのに比べて見やすいし、より厳密にフォーマットに従わせることができるのでよさそう。

Salesforce: Dynamic Interactionsとは

これのこと。5/12の記事。

TechCrunchにも情報がありますね。

読んでみたけど、今までと何が変わるのかよくわかりませんでした。

というわけで、ググるとこちらの動画が見つかります。

(動画の作成日が4月になってるので、今回のリリースとの関係性は不明)

これを見るに、ポイントとしてはこのあたりでしょうか。

f:id:dackdive:20210513141424p:plain

コンポーネントの設定画面に「Events」というタブが新たに追加されており、"Add Interaction"をクリックすると

f:id:dackdive:20210513141419p:plain

こういった形で、

  • このコンポーネントから発火されるイベントをどのコンポーネントに伝えるか
  • そのときの各種パラメータはどうするか(デモでは数式っぽい記法でイベントオブジェクトから取り出してるように見える)

といったことを「ユーザー側が」「GUIで」設定できるようになります。

従来ですと開発者がゴリゴリコーディングしないと実現できなかった部分であり、またコンポーネント開発側でなくそれを使う側がイベント部分だけを実装するということは(おそらく)不可能だったはず。

また、設定項目に "interaction" というドロップダウンが見えますが、動画の最後のスライドによると

f:id:dackdive:20210513141625p:plain

コンポーネントにイベントを伝達する以外にも、特定のURLを開く・SMSを送る・外部APIを実行する、などができそう。

いつリリースされるの?

公式記事の方では

Dynamic Interactions is expected to be generally available in Winter 2021.

また、TechCrunchの記事によれば

Dynamic Interactionsは、Salesforceからベータ版として米国時間5月12日より提供される。この製品が一般公開されるのは、秋のユーザー会議Dreamforce(ドリームフォース)の頃になる予定だ。

だそうです。

ベータって言ってるけど、少なくともSummer'21リリースノートには情報載ってないですよね...?

https://help.salesforce.com/articleView?id=release-notes.salesforce_release_notes.htm&type=5&release=232

余談

Dynamic Interactions という言葉自体は昨年の Dreamforce (DreamTX)で登場していたように見える。

RustのFromトレイトとIntoトレイト

先日こんな記事を書いたのだが、最後に登場した From トレイトというものをよく理解してなかったのでその後調べたメモ。

検索したところ Web 上では Rust by Example ぐらいしかヒットしなかった。

また、以前読んだ「実践 Rust 入門」にもさらっとだが触れられていた。

(8-6-2 std::convert::From)

概要

この2つのトレイトはどちらも、ある型から別の型に変換するためのトレイトである。

たとえば、String 型の文字列を定義するときによく使う

let s = String::from("hello");

String::from()From トレイトを利用している。

また、この2つのトレイトは密接にリンクしており、From トレイトを実装すると自動的に Into トレイトも実装されるという関係になっている。ただし逆はない。

From トレイト

From トレイトは

impl From<T> for U {
  fn from(T) -> Self;
}

というように定義されていて、これを実装すると T 型から U 型への変換ができるようになる。

以下、サンプルコード。Rust by Example と同じ。

use std::convert::From;

#[derive(Debug)]
struct Number {
    value: i32,
}

// `i32` 型から `Number` 型へ変換できるようになる
impl From<i32> for Number {
    fn from(item: i32) -> Self {
        Self { value: item }
    }
}

fn main() {
    let num = Number::from(30);
    println!("My number is {:?}", num);
}

Into トレイト

先ほどの

impl From<T> for U {
  fn from(T) -> Self;
}

を実装することによって、自動的に Into トレイトも実装されたことになり、

T型の値.into()

という書き方で U 型に変換できるようになる。

fn main() {
    // From トレイトを使うとこういう書き方だったが
    let num = Number::from(30);
    println!("My number is {:?}", num);

    // Into トレイトにより、こう書くこともできる
    // 変数の方の型を省略するとエラーになる
    let num: Number = 30.into();
    println!("My number is {:?}", num);

}

しばらく勘違いしてたのだが、 FromInto が対になってて T ←→ U 間で相互に変換可能になるわけではなく、変換の方向としてはどちらも同じ(サンプルでいうと、 i32Number)。

なお、上のサンプルでは変数の方に型を書いているが、メソッドチェーンなどでそれができないときは

let num = Into::<Number>::into(5);

のようにも書ける。

参考にさせていただいた記事:

From トレイトと Into トレイト、どちらを実装すべき?

std::convert::From - Rust

によれば

One should always prefer implementing From over Into because implementing From automatically provides one with an implementation of Into thanks to the blanket implementation in the standard library.

なので、基本は From トレイトを実装すると理解しておけばよさそう。

また、じゃあどういうときに Into トレイトの方を使うのか?についても直後に書かれており

Only implement Into when targeting a version prior to Rust 1.41 and converting to a type outside the current crate. From was not able to do these types of conversions in earlier versions because of Rust's orphaning rules. See Into for more details.

Rust のバージョンが 1.41 より前は、変換先の型が現在のクレートの外にある場合 From トレイトではコンパイルエラーになっていたらしい。

U::from(t)t.into() どちらで書くべき?

わからない。好みの問題な気がする。
Into トレイトの最後で触れたように変数の方に型を書けないケースとかを考えると U::from(t) の方が好み。

余談: TryFromTryInto

似たようなトレイトとして、TryFromTryInto というのもあるらしい。

TryFrom and TryInto - Rust By Example

これは、やっていることは同じようにある型から別の型への変換だが、失敗する可能性があるときはこちらを使う。

use std::convert::TryFrom;

#[derive(Debug, PartialEq)]
struct EvenNumber(i32);

impl TryFrom<i32> for EvenNumber {
    type Error = ();

    fn try_from(value: i32) -> Result<Self, Self::Error> {
        if value % 2 == 0 {
            Ok(EvenNumber(value))
        } else {
            Err(())
        }
    }
}

失敗する可能性があるので try_from() の戻り値は Result 型になっている。

リファレンス