Episodit
-
第53章へようこそ。
今日のテーマは「生ポインタ」です。
第52章で、unsafeが解禁する5つの操作を見ました。
その1つ目が、生ポインタのデリファレンスだった。
生ポインタは、参照と違って借用規則もライフタイムの保証も持たない、ただのアドレスだ。
そう触れただけで、生ポインタそのものは詳しく見ていない。
参照は、借用規則とライフタイムに守られていた。
第1章から見てきた借用、第13章から見たライフタイム。
参照は、その規則に守られた、有効な値へのアクセスだ。
メモリの上では、そのアクセスは1つのアドレスとして表される。
生ポインタは、そのアドレスから借用規則とライフタイムを取り去ったものだ。
今日扱うのは、生ポインタが参照と何が違うのか、なぜ作ることは安全でデリファレンスがunsafeなのか、そしてヌルとNonNullです。
-
第52章へようこそ。
今日のテーマは「unsafe」です。
第51章の最後で、コンパイラの検査には外側があると見ました。
Pinが移動を止める仕組みは、型が可変参照を返さないところまではコンパイラの検査の中にある。
だが、Pinを作る操作と中身を取り出す操作には、が動かないという前提があり、その前提が本当に成り立つかをコンパイラは検査しない。
検査されないその一点は、操作を書いた人間が引き受けた。
この「検査の外を人間が引き受ける」という形は、Pinだけのものではない。
Rustには、検査の外に出て、その正しさを人間が引き受けることを示す、1つのキーワードがある。
それがunsafeです。
今日扱うのは、unsafeが実際に何をするのか、何を解禁するのか、そしてなぜそれが「安全を捨てる」ではなく「保証する主体が移る」ことなのか、です。
-
Puuttuva jakso?
-
第51章へようこそ。
今日のテーマは「Pin」です。
第45章で、Pinという型に触れました。
asyncをつけた関数から作られるFutureは、状態の中に自己参照を持つことがある。
自己参照を持つFutureは、メモリ上で別の場所へ移動すると、内部のポインタが古い場所を指したまま残ってずれる。
これを避けるのがPinだと見ました。
今日扱うのは、そのPinが移動を止めるとき実際に何をしているのか、なぜFutureへの問い合わせがPinを通して行われるのか、そしてなぜほとんどの型はPinと無関係でいられるのか、です。
-
第50章へようこそ。
今日のテーマは「ブロッキングと協調スケジューリングの衝突」です。
第44章で、タスク自身が進行を譲る協調的なスケジューリングを見ました。
第46章で、1つの流れの中で複数のタスクを管理して進める実行器を見ました。
この2つを積み上げると、1つの帰結が導かれます。
awaitで進行を譲らない重い処理をタスクの中に置くと、同じ流れを共有するほかのタスクが進まなくなる。
今日扱うのは、なぜそれが起きるのか、そしてそれをどう避けるのか、です。
-
第49章へようこそ。
今日のテーマは「Stream」です。
第4章で、複数の値を1つずつ取り出すイテレータを見ました。
第45章で、いずれ手に入る1つの結果を表すFutureを見ました。
イテレータは複数の値を扱い、Futureは時間をかけて届く1つの値を扱いました。
今日扱うのは、この2つが1つに合わさったもの、です。
複数の値が、時間をかけて1つずつ届く。
この流れを表す型を、Streamと呼びます。
今日は、イテレータとFutureをもう一度たどり、その2つがどう合わさってStreamになるのか、Streamから値をどう受け取るのか、そして値とイテレータの関係が、FutureとStreamの間にそのまま成り立つことを、見ていきます。
-
第48章へようこそ。
今日のテーマは「待ち合わせ」です。
第45章で、asyncをつけた関数が返すFutureという、いずれ手に入る結果を表す値を見ました。
第46章で、そのFutureに問い合わせを繰り返して完了まで進める実行器を見ました。
第47章で、タスクを途中でやめるキャンセルが、Futureをドロップすることだと見ました。
ここまで扱ってきたのは、いつも1つのFutureでした。
今日扱うのは、複数のFutureを同時に待つこと、です。
具体的には、複数のFutureを1つにまとめて全部の完了を待つjoin、最初に完了した1つだけを待って残りを捨てるselect、そしてこの待ち合わせが、これまで見てきた待ちとどう違うのか、を扱います。
-
第47章へようこそ。
今日のテーマは「タスクのライフサイクル」です。
第44章で、1つの流れの中で複数のタスクを交互に進めることで並行を作る、非同期処理を見ました。
第45章で、asyncをつけた関数が返すFutureという、いずれ手に入る結果を表すあたいを見ました。
第46章で、そのFutureにpollを繰り返し呼んで完了まで進める実行器を見ました。
今日扱うのは、タスクが途中でパニックしたとき何が起きるか、そしてタスクを途中でやめるキャンセルという操作が何を意味するか、です。
-
第46章へようこそ。
今日のテーマは「実行器」です。
第45章で、Futureは自分自身を進める手段を持たないと見ました。
pollを繰り返し呼んで完了まで進める主体は、Futureの外にいる。
今日扱うのは、その外にいる主体が何か、それがどうやって無駄なくpollを繰り返すのか、そしてRustがその主体を標準では持たない設計です。
-
第45章へようこそ。
今日のテーマは「Future」です。
第44章で、asyncをつけた関数が、中断と再開ができる関数になることを見ました。
asyncをつけた関数を呼ぶと、結果そのものではなく、Futureと呼ばれる値が1つ返ってきます。
今日扱うのは、このFutureが何を表すのか、外からの問い合わせにどう答えるのか、そして中断した地点までの進行をどこに保持するのか、です。
-
第44章へようこそ。
今日から、非同期処理を扱う一連の章に入ります。
今日のテーマは「非同期入門」です。
これまでの一連の章で見たのは、スレッドによる並行処理でした。
スレッドを使って流れを複数に分け、それぞれの流れを別々に進める方法です。
非同期処理は、並行処理を作るもう1つの道です。
スレッドとは異なる方法で、複数の処理を同時に進行させます。
今日扱うのは、なぜスレッドとは別の道が要るのか、並行と並列という2つの言葉が指す違い、そして非同期がどちらの軸に位置するのか、という基本構造です。
-
第43章へようこそ。
今日のテーマは「デッドロック」です。
ここまで、複数のスレッドが安全に値を共有するための手段を見てきました。
MutexやRwLockは、ロックを取った流れだけに接触を許し、ほかの流れを待たせることで、同時の書き換えを防いだ。
ロックは、待たせることで安全を作る仕組みでした。
しかし、待たせることそのものが、新しい停止を生むことがあります。
複数の流れが、互いに相手の解放を待ち、どちらも進めなくなる。
この、互いを待ち続けて永遠に進まない状態を、デッドロックと呼びます。
デッドロックは、Rustがコンパイル時に防がない種類の問題です。
今日扱うのは、デッドロックがなぜ起こるのか、なぜ型システムがそれを防がないのか、
そして、取得の順序を統一することがなぜ回避になるのか、という思考法です。
-
第42章へようこそ。
今日のテーマは「アトミック」です。
これまでの章で、共有した値を複数のスレッドから安全に書き換える手段として、ロックを見てきました。
MutexやRwLockは、ロックを取った流れだけに値への接触を許し、ほかの流れを待たせる。
ロックを取り、解放するという手順そのものにコストがかかる。
しかし、共有したい値が1つの数や1つの真偽の値のように単純なとき、ロックでひとくくりに囲むのは大げさです。
ロックを使わずに、その1つの値の更新だけを安全に行う手段がある。
それがアトミックです。
第37章と第39章で、参照カウントの操作が「原子的」であるかどうかを見ました。
今日は、その「原子的」を正面から扱います。
今日扱うのは、アトミックが何を保証するのか、メモリ順序という付随する指定、そしてアトミックで扱える範囲の限界です。
-
第41章へようこそ。
今日のテーマは「RwLock」です。
前章のミューテックスは、読み取りでも書き換えでも、一度に1つの流れだけにあたいへの接触を許しました。
触れる目的が読み取りであっても、ロックを取った1つの流れが終えるまで、ほかの流れは待つ。
しかし、複数の流れが同じ値をただ読むだけなら、同時に触れても問題は起こりません。
第37章で見たように、読み取りしか起こらないなら、書き換えによる競合は生じないからです。
読み取りどうしは衝突しないのに、Mutexはそれも1つずつに制限する。
読み取りが多い場面では、この制限が並行性を必要以上に下げる。
RwLockは、読み取りと書き換えを区別するロックです。
読み取りは複数の流れに同時に許し、書き換えだけを1つの流れに絞る。
排他の範囲を、接触の目的によって変える。
今日扱うのは、RwLockが提供する2種類のロック、その取得の規則が借用の規則とどう重なるのか、
そして、どういう場面でこの区別が効くのか、です。
-
第40章へようこそ。
今日のテーマは「Mutex」です。
前章で、Arcが共有された読み取りのアクセスを与えることを見ました。
複数のスレッドが1つの値を共通の所有者として持ち、それぞれが読める。
ただし、Arcが解決するのは「誰がその値を所有するか」までで、「いつその値を書き換えてよいか」は別の問題でした。
複数のスレッドが同じ値を同時に書き換えれば、書き込みが互いに干渉し、データ競合が起こる。
Mutexは、この「いつ書き換えてよいか」を解く仕組みです。
1つの値に対して、一度に1つのスレッドだけが触れることを保証する。
触れている流れがいる間、ほかの流れは待つ。
この「一度に1つだけ」という保証を、排他アクセスと呼びます。
今日扱うのは、Mutexが排他アクセスをどう保証するのか、それを支えるガードの仕組み、
そしてArcと組み合わせて複数のスレッドで可変な値を共有する形です。
-
第39章へようこそ。
今日のテーマは「Arc」です。
ここまで、並行処理で値を扱う道は、所有権を移すことでした。
第36章のmoveは、値の所有権を新しいスレッドへ移した。
第38章のチャネルは、値の所有権を流れの間で送って受け渡した。
どちらも、値は常にどこか1つの流れにあり、移ることで次の流れへ渡っていく。
しかし、値を移すのではなく、1つの値を複数のスレッドが同時に保持したい場面があります。
大きな値を、それぞれのスレッドへ複製して配るのは無駄が大きい。
1つの値を、複数のスレッドが共通の所有者として持ちたい。
ここから、所有権を渡さずに共有する道に入ります。
今日扱うのは、複数のスレッドが1つの値を共有して所有するための型、Arcです。
第22章のRcを振り返りながら、なぜRcではスレッドを越えられないのか、
Arcがそれをどう解決するのか、その代償は何か、という核心を見ていきます。
-
第38章へようこそ。
今日のテーマは「チャネル」です。
第36章で、moveが値の所有権を新しいスレッドへ移すことを見ました。
あのときの移動は、スレッドを生成する一度きりのものでした。
クロージャに渡した値が、新しいスレッドへ移って、そこで使われる。
チャネルは、この所有権の移動を、スレッドが動いている間ずっと繰り返せるようにする仕組みです。
1つの流れが値を送り出し、別の流れがそれを受け取る。
送り出した側は、その値の所有権を手放す。
受け取った側が、新しい所有者になる。
スレッドを生成するときの一度きりの移動が、実行の途中で何度でも起こる形に広がる。
これは、第36章のmoveの延長です。
所有権を移して受け渡すという同じ考え方が、動き続ける流れの間の通信に使われる。
今日扱うのは、チャネルがどう所有権を運ぶのか、受け取る側がどう待つのか、
複数の送り手をどう束ねるのか、そして送る速さと受け取る速さをどう調停するのか、です。
-
第37章へようこそ。
今日のテーマは、SendとSyncです。
前章で、スレッドに値を渡すときは所有権ごと渡すことを見ました。
moveが、外側の値の所有権をクロージャへ移し、新しいスレッドへ持ち込む。
ただし、すべての値がスレッド境界を越えてよいわけではありません。
ある値をスレッドに渡すと、データ競合という危険が生じる場合がある。
データ競合は、複数の流れが同じ値に同時に触れ、少なくとも一方が書き換えるときに起こる、結果の定まらない状態のことです。
Rustは、この危険を実行してから気づくのではなく、コンパイルの時点で防ぐ。
そのために、どの型がスレッド境界を越えてよいかを、型のうえで区別する。
この区別を担うのが、SendとSyncという2つのトレイトです。
今日扱うのは、SendとSyncがそれぞれ何を表すのか、これらがどう自動的に決まるのか、
そして、なぜRcがこの境界を越えられないのか、という核心です。
-
第36章へようこそ。
今日から、並行処理を扱う一連の章に入ります。
最初のテーマは「スレッド」です。
スレッドは、プログラムの中で独立して進む実行の流れのことです。
1つのプログラムが複数の流れを同時に持つと、別々の処理が並行して進む。
並行処理は、所有権の規律が新しい場面に置かれる領域です。
これまで所有権は、1つの実行の流れの中で、値がいつ生きていつ解放されるかを決めてきました。
スレッドが増えると、値が2つ以上の流れにまたがる可能性が出てくる。
値が流れをまたぐとき、誰がそれを所有し、いつ解放するかを決める規律が要る。
Rustはこれを、型と所有権の規律で扱います。
今日扱うのは、スレッドの生成、スレッドの終了を待つ仕組み、
そして、なぜスレッドに渡す値は所有権ごと渡さなければならないのか、という核心です。
-
第35章へようこそ。
今日のテーマは「手続き的マクロ」です。
これまでの3章で扱ってきたのは宣言的マクロでした。
macro_rules!の宣言で、パターンと展開の組をルールとして並べる。
入力のトークンがパターンに照らされ、対応する展開が呼び出し位置に差し込まれる。
宣言的マクロには表現の限界があります。
パターンと展開の対応で書ける範囲を超えた変換、たとえば構造体のフィールド名を文字列として取り出して別のコードに埋め込む、こうした処理は宣言的の枠を超える。
これを扱うために用意されているのが、今日扱う手続き的マクロです。
今日扱うのは、宣言的マクロの限界、手続き的マクロが解く問題、3つの形態、トークンストリームという界面、synとquoteによる分業、そして別クレートとしてコンパイルされる構造的な理由です。
-
第34章へようこそ。
今日のテーマは「繰り返しと衛生性」です。
前章で、フラグメント指定子がトークン列の中の特定の位置を捕捉する仕組みを見ました。
式を1つ、識別子を1つ、型を1つ、それぞれ捕捉する。
固定の個数なら、これだけで足りる。
しかしマクロは、任意個のトークンを受け取りたい場面がある。
vecマクロは要素を何個でも受け取って配列を作る。
また、マクロが導入した変数の名前が、呼び出し側の変数の名前と衝突しないように、識別子のスコープにも工夫が要る。
今日扱うのは、可変長のトークンを扱う繰り返し構文、
繰り返しでは難しい変換を担う再帰的展開、
マクロの識別子が呼び出し側に漏れない衛生性、
そしてマクロ内のパスがクレート境界を越えても壊れない仕組みです。
- Näytä enemmän