1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cloudflare を使い倒す worker-rs 編

1
Last updated at Posted at 2025-12-21

年の瀬、忘年会とかが特にあるわけでもなく「税制変わったな~」と思うくらいの @toreis です。

先日 Cloudflare Workers の記事でこんなことを言及しました。

詳細は他の記事を参照いただきたいですが、WebAssembly はいろんな言語で書いたものをコンパイルして WebAssemblyにするので、例えば C, C++, C#, Rust, Go, Kotlin, etc...で書いたコードも WebAssembly になります。

つまり、V8 で動かせて怪しくないアプリなら何でも Workers で動かせるのです。やったぁ!

そして、Rust をちゃんと触ったことがない私は思ったのです。
「Workers を Rust で書いてみたい」と。

ということで、Todo リストを作ってると明日が来ちゃうので、どういう仕組みで動いているのかを理解します。

とりあえず Hello, World!

Workers を Rust で作ってみます。なお、Rustup は入っている前提です。

まずは target を設定します (Workers で動かすための設定) 。

rustup target add wasm32-unknown-unknown

そして、GitHub からテンプレを引っ張ってくるために cargo-generate を入れます。

cargo install cargo-generate

準備できました。
そしたら Clone してきます。

cargo generate cloudflare/workers-rs

そうするとどのテンプレートを使うか聞いてくれるので、ここでは templates/hello-world を使います。

 Favorite `cloudflare/workers-rs` not found in config, using it as a git repository: https://github.com/cloudflare/workers-rs.git
?  Which template should be expanded? ›
  templates\axum
❯ templates\hello-world
  templates\hello-world-http
  templates\leptos

Project Name は適当に設定します。

そしたらば、Project Name と同名のディレクトリの中にソースファイルなどが一式詰まってプロジェクト出来上がりです。

src/lib.rs を見てみると、いかにも Hello, World! を返すコードが見られます。

src/lib.rs
use worker::*;

#[event(fetch)]
async fn fetch(
    _req: Request,
    _env: Env,
    _ctx: Context,
) -> Result<Response> {
    Response::ok("Hello World!")
}

さて、プロジェクトをビルドして実行する前に、wrangler.toml をいじります。

wrangler.toml
name = "hello-workers-rs"
main = "build/index.js"
compatibility_date = "2025-12-21"

[build]
- command = "cargo install -q worker-build@^0.7 && worker-build --release"
+ command = "cargo install -q worker-build@^0.7.2 && worker-build --release"

パッチバージョンが書いておらずバージョン解析に失敗するので、今日時点で最新の 0.7.2 にしておきます。

書き込んだら、npx wrangler dev で実行してみます。

ブワーッとビルドして、build ディレクトリに成果物が詰まっていることが確認できます。

その中に index.js もあります。これが Workers の実行ファイルになりそうですね。

ビルドが終わると、ローカルサーバーが実行されて http://127.0.0.1:8787 で待機します。

アクセスしてみると、この通り。

image.png

今日びよく見かける Hello, World! です。

いや、何が起きてん

Rust のコードが動いています。謎です。V8 て TypeScript エンジンちゃうんか。

これについては、ビルド成果物を見てみるとしましょう。

build ディレクトリには、Workers のエントリポイントである index.js、Rust でつくったやつのコンパイル結果と思われる index_bg.wasm が見られます。

build/index.js (抜粋)
import{WorkerEntrypoint as ct}from"cloudflare:workers";import D from"./index_bg.wasm";
build/index.js (抜粋)
function q(){s++,p=null,F=null,typeof numBytesDecoded<"u"&&(numBytesDecoded=0),typeof g<"u"&&(g=0),r=new WebAssembly.Instance(D,$).exports,r.__wbindgen_start()}

後者のコードブロックに注目してください。

r=new WebAssembly.Instance(D,$).exports

という記述、これは WebAssembly Instance を作るという、紛れもない Wasm の実行始めです。

そしてトドメが

r.__wbindgen_start()

です。
Wasm 動かしてらぁ!です。

そして、実際には r という変数にはインスタンスの exports、すなわち Wasm がエクスポートしてる関数や変数群が代入されていて、そのうちの __wbindgen_start() が実行されています。
名前からしていかにもですね。

で、実際に fetch されるときには、以下のコードが動いているのではないか、と推測します。

function B(t,e,n){return r.fetch(t,e,n)}

var z=class extends ct{}
z.prototype.fetch=function(e){return B.call(this,e,this.env,this.ctx)}

r.fetch() は Wasm で定義した Fetch を呼ぶための関数と思われます。

そしてそれを B でラップしたあと、z (WorkerEntrypoint) の fetch を実行するのに必要な情報を追加して呼び出します。(多分)

おわりに

Rust が Wasm になって実行されているということがわかりました。

動く仕組みについても、浅い理解なりに追うことができました。

何が嬉しいねん

さて、Workers を Rust で書くことの嬉しさってなんなんでしょう。

まあまずは Rust にあって TypeScript にないものなーんだ?って話が有力です。
所有権とかもろもろありますが、私の浅い理解だと「Rust 使っときゃとにかく安全で高速」というイメージがあります。

Linux カーネルにも組まれるくらいには安全なのでしょう、多分。
実際、とある案件で Rust を使っていたとき、挙がってくるバグレポートはフロントばっか、バックはレポート 0 ですげーと思った記憶があります。

また、CPU マターの処理はいろんな言語と比較して高速ですので、例えば動画のエンコードとかも Workers に乗るかもしれませんね!:boom: (爆発オチ)

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?