2
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?

Rust 1.99が来たので、実務で使えそうな変更と気をつけたいところを拾ってみる

2
Last updated at Posted at 2026-10-03

Rust 1.99.0 が2026年10月1日にリリースされました。

今回は新文法ドーン!みたいな派手なアップデートではありません。

ただ、中身を見ると、

  • FFI
  • raw pointer
  • メモリ所有権
  • unsafe
  • ファイルシステム
  • Cargo

あたりを触る人には、地味ながら面白い変更がいくつか入っています。

Rust 1.99の主な変更として、C可変長引数関数の定義、raw pointerからのlayout取得、Box / VecのポインタベースAPI、UTF-8変換、ファイルtimestamp操作などがstableになっています。

何言ってんだかよく分かんないですよね。

少し噛み砕いて言うと、

RustとCの境界を書きやすくなったり、メモリの所有権を低レベルに扱うための道具が増えたり、壊れたUTF-8を扱いやすくなったり、symlinkを意識したファイル操作が増えたりしています。

そして個人的には、新機能そのものよりこちらの方が気になりました。

今まで「まあ動いてるしヨシ!」で通していたunsafe周辺について、Rust側が安全条件をさらに細かく言語化してきています。

なので今回の1.99は、
新しい便利機能が大量に増えたというより、
低レイヤー処理の工具箱が少し増えて、ついでに取扱説明書も細かくなったという印象です。

普通のアプリケーションを書いている人が、突然全部書き換える必要はありません。

一方で、

  • FFI
  • parser
  • DB
  • filesystem
  • 大きなbufferを扱う処理
  • unsafe Rust

あたりを触っているなら覗いてみる価値がありそうです。

Rust側でC ABIの可変長引数関数を書けるようになった

Rustは以前からCの、

printf(...)

のような可変長引数関数を呼べました。

1.99では逆に、"C" / "C-unwind" ABIの可変長引数関数をRust側で定義できるようになりました。

場合によっては、

C
↓
Cで書いた中継コード
↓
Rust

だったものを、

C
↓
Rust

まで縮められます。

一枚壁が消えました。

古いCライブラリ、plugin API、OS周辺などとの接続では使い道がありそうです。

ただし、Rustで書いたからC variadicが急に安全になったぞ! という話ではありません。

危ないものは危ない。

Cとの境界をRust側まで直接持ってこられるようになった、と考えるのがよさそうです。

詳しくはRust 1.99公式リリースの extern "C" variadics を参照。

VecやBoxを低レベルな形へ分解しやすくなった

今回

Vec::into_parts
Vec::from_parts

Box::into_non_null
Box::from_non_null

などがstableになりました。

例えばVecを、

pointer
length
capacity

に分解し、あとから再構築できます。

何が嬉しいかというと、

  • FFI
  • buffer pool
  • codec
  • parser
  • DB
  • native libraryとの受け渡し

などで、既にあるメモリを低レベルな形へ分解し、所有権を管理しながら受け渡す設計を作りやすくなります。

つまり、

巨大データ
↓
コピー
↓
別ライブラリ
↓
またコピー
↓
さらにコピー

みたいな状況で、不要なコピーを避けるための道具として使える可能性があります。

メモリ「おなかいっぱいでざる_(:3 」∠ )_」となるやつです。

ただし、これらのAPIを使っただけで自動的にzero-copyになるわけではありません。

さらにVec::from_partsなどでは、

  • pointerが正しいallocationを指している
  • alignmentが正しい
  • lengthとcapacityが正しい
  • 初期化済み領域が正しい

などの条件を呼び出し側で保証する必要があります。

なので、高速化魔法が増えたというより、低レベルな所有権管理を正しく表現する工具が増えた

くらいがよさそうです。

詳しくは公式の Vec::into_parts / Vec::from_parts を参照。

raw pointerのまま動的なサイズを調べられるようになった

今回、

Layout::for_value_raw
size_of_val_raw
align_of_val_raw

がstableになりました。

特に面白いのは、sliceやtrait objectなど、実行時までサイズが決まらない型(DST)について、raw pointerをreferenceへ変換せずにsizeやalignmentを取得できるところです。

allocator、FFI、独自bufferなどを扱っている場合に使い道がありそうです。

逆に、

let a = 1;
let b = 2;
println!("{}", a + b);

くらいの世界で生きている場合は、まず気にしなくて大丈夫です。

なお、これらはunsafeです。

例えばsliceならpointer metadataに含まれるlengthが妥当であること、trait objectならvalidなvtableを持っていることなど、呼び出し側に条件があります。

「raw pointerなら何でもサイズを調べられるようになった!」

ではないので、そこは注意です。

詳しくは公式の Layout::for_value_raw、size_of_val_raw、align_of_val_raw を参照。

壊れたUTF-8をStringへ持っていきやすくなった

String::from_utf8_lossy_owned(...)

もstableになりました。

要するに、

外部からbyte列を受け取った
↓
UTF-8が少し壊れていた
↓
壊れたところを � に置換
↓
Stringとして処理続行

という処理がやりやすくなります。

ログ、crawler、文書ingestion、外部コマンド出力など、

データが1か所壊れていたくらいで全処理を爆散させたくない

用途では便利そうです。

世の中のデータは意外と汚いです。

「当然UTF-8だよね!」

と信じて突っ込むと、たまに後ろから刺されます。

ただし�へ置換した時点で元のbyte情報は失われます。

なので、

  • 検索用テキスト
  • 表示用ログ
  • ingestion継続用

などには向いていますが、

  • 原文保存
  • byte完全一致
  • 証跡

には向きません。

また、入力したVec<u8>のallocationが必ずそのまま再利用される、という保証でもありません。

ここも「なんでもzero-copy!」ではありません。

詳しくは公式の String::from_utf8_lossy_owned を参照。

symlinkを辿らずtimestampを変更できる

std::fs::set_times
std::fs::set_times_nofollow

もstableになりました。

set_timesはpathがsymbolic linkならリンク先を操作します。

一方、

set_times_nofollow

ではリンクを辿らず、symlinkそのものを対象にします。

設定できるのはaccess timeとmodification timeです。

例えば、

  • backup
  • archive展開
  • sync tool
  • package manager
  • file copier

などでは使い道がありそうです。

ファイルシステムを扱っていると、

このpathを操作します
↓
実はsymlinkでした
↓
別の場所を触ってました

という笑えない罠があります。

そのため、

「symlinkを辿る」「辿らない」をAPI上で明示できる

のは普通にありがたいです。

もちろん、これ一本でTOCTOUやsymlink raceが全部滅びるわけではありません。

そんな都合のいい聖剣ではありません。

詳しくは公式の std::fs::set_times_nofollow を参照。

Cargoにも割と実務的な変更がある

Rust本体より、こちらの方が日常的に効く人が多そう。

CIではincremental compilationがデフォルト無効に

Cargo 1.99から、CI環境変数が設定されている環境ではincremental compilationがデフォルトで無効になります。

incremental compilationはローカル開発では便利です。

前回のbuild結果を利用して、変更部分だけサクッとbuild。

ありがたい。

ただし毎回ほぼクリーンな環境でbuildするCIでは、

incremental用の情報を作る
↓
CIジョブ終了
↓
さようなら

になりがちです。

悲しい。

そこでCargo側が、

ローカル開発
→ incremental

CI
→ non-incremental

という使い分けをするようになりました。

CIでbuild時間を継続計測している場合は、Rust/Cargo 1.99への更新前後でbuild条件そのものが変わる可能性があることには注意した方がよさそうです。

詳しくはCargoの該当変更 #17220 を参照。

debug profileが追加された

Cargoに新しいbuilt-in profileとして、

debug

が追加されました。

現時点ではdevとdebugに違いはありません。

では、なぜ追加したのか。

Cargo公式によると、将来的にdev profileを「debugging中心」から、より速いdevelopment iteration向けのdefaultへ移していくための準備です。

つまり今は、

dev ≒ debug

ですが、

「devとdebugはずっと同じもの」

という前提は今後崩れる可能性があります。

今すぐ設定を書き換える必要はありません。

ただ、build profileを厳密に管理しているprojectでは頭の片隅に置いておいてもよさそうです。

詳しくはCargoの該当変更 #17214 を参照。

Workspaceのdefault-features制御も改善

Edition 2024以降では、

serde = { workspace = true, default-features = false }

のように、workspaceから継承したdependencyについてもmember側からdefault-featuresをoverrideできるようになりました。

例えば、

workspace
├─ Desktop
├─ CLI
├─ Server
└─ Core

のような構成で、

Desktop → 全部入り
CLI     → 少し軽く
Core    → もっと軽く

としたい場合に扱いやすくなります。

compile timeやbinary sizeを詰めるときにも使い道がありそうです。

ただし複数packageを同時にbuildした場合にはdependency featureのunification自体は引き続きあります。

なので、

「よし、workspace memberごと完全に独立したfeature世界だ!」

ではありません。

Cargo「そこまでは言ってない」

です。

また、Edition 2024より前ではmember側のdefault-features = falseは無視され、warningになります。

詳しくはCargoの該当変更 #17126 を参照。

既存コードで少し気になったところ

ここからは新機能というより、

1.99を機に昔のコードをちょっと掘り返してもよさそうなところ

です。

Box::leakしたものを後から回収している場合

例えば、

Box
↓
Box::leak
↓
'static referenceとして利用
↓
あとでBoxへ戻して解放

みたいな技巧的コード。

1.99で突然このコードが禁止されたわけではありません。

変更されたのは主に公式ドキュメント上のguidanceです。

後で解放するつもりなら、

Box::into_raw

とか、

Box::into_non_null

などを使い、最初からpointerとして所有権を管理する方が推奨されています。

つまり、leakしたのに後から「やっぱ返して」は避けようという話です。

名前がleakですし。

古いunsafeコードにBox::leakがあるなら、一度検索してみてもよさそうです。

詳しくはRust 1.99公式リリースの Box::leakに関する変更 を参照。

raw pointerを作るためだけに一瞬referenceを作るコード

新しいlintとして、

raw_borrows_via_references

が追加されました。

例えば、

&(*ptr).field as *const _

のような、

raw pointer
↓
reference
↓
またraw pointer

というコードです。

「最後pointerなんだからいいじゃん」

となりそうですが、Rustではそう単純ではありません。

referenceを作った時点でaliasingなどのvalidity requirementsが発生します。

最終的にraw pointerしか必要ないなら、

&raw const (*ptr).field

のように、最初からraw borrowを書く方が意味も正確です。

このlintはRust 1.99時点ではallow-by-defaultです。

unsafeを多く使うprojectなら、

#![warn(raw_borrows_via_references)]

を一度有効にしてみるのもよさそうです。

何も出なければ平和。

出たら昔の自分との対話が始まります。

「なぜ私はこんなコードを書いた」

というやつです。

詳しくはrustc公式ドキュメントの raw_borrows_via_references を参照。

消費済みRangeInclusiveを再利用している場合

0..=10のようなRangeInclusiveについて、一部のiterationがよりよく最適化されるようになりました。

その副作用として、iteratorとして最後まで消費した後のRangeInclusiveの挙動が以前と変わる場合があります。

例えば、

rangeを最後までiterate
↓
その後start()やend()を見る

場合や、消費済みrangeをslice indexとして使う場合です。

これらの挙動は元々stableなものとして保証されていませんでした。

なので、IteratorはIteratorとして使い切って終わりくらいにしておくのが安全です。

Iterator「私の亡骸から状態を取らないでください」という感じです。

詳しくは Rust 1.99 Release Notes と RangeInclusive公式ドキュメント を参照。

個人的には今回ここが面白かった

Rust 1.99を見ていて個人的に面白かったのは、新しいAPIそのものよりこちらでした。

Rustは、

unsafeを無くす

というより、

unsafeなら
何を保証しなければならないのかを
もっと細かく言語化する

方向に進んでいるように見えます。

今回だけでも、

referenceを一瞬作るだけでも意味がある

leakしたものを後から回収するなら別の方法を使う

Vecを分解できるがownershipの正しさは自分で保証する

raw pointerをreferenceへ変換せず扱える場面が増える

という変更が並んでいます。

低レイヤーでは、

動いた

と、

仕様上正しい

は同じではありません。

ここはRustを書いていると何度も殴られるところです。

昨日まで動いてた?

それはよかった。

でも、それが仕様上正しいとは誰も言っていない、みたいな。

怖い。

ただ逆に言えば、Rustはこの曖昧な境界を少しずつ表へ出してくれているとも言えます。

まとめ

Rust 1.99は、普通のアプリケーションコードが突然全部変わるようなアップデートではありません。

ただ、

  • Cとの境界
  • pointer ownership
  • filesystem
  • unsafe
  • Cargoのbuild環境

あたりを触る人には、かなり味のある変更が入っています。

個人的に応用できそうだと思ったのは、

巨大データ処理で不要なコピーを減らす設計

FFI境界の単純化

壊れた入力データでも止めないingestion

symlinkを意識したfile操作

workspaceごとのfeature整理

あたり。

そして既存コードでは、

Box::leak

raw pointer周辺で一瞬referenceを作る処理

消費済みRangeInclusive

CIのbuild時間比較

あたりを軽く確認しておいてもよさそうです。

今回は大きな花火というより、

Rustの低レイヤー部分に新しい工具が増えて、危険箇所に「ここ足元注意」の看板が何枚か追加された

くらいのアップデートでした。

しかしほんとRustは日本語情報すくないっすねー。AI様に確認・翻訳してもらわないと読み切る事すら難しい…というか無理でした。

あーつかれた


ちなみに、Rustリリースを日本語で継続的に追っている方をご紹介。
Rustのリリース内容をかなり丁寧に追っています。

是非こちらも参考に。おすすめ。

2
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
2
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?