あずんひの日

あずんひの色々を書き留めるブログ

Rust 1.76を早めに深掘り

本日2/9(金)にリリースされたRust 1.76の変更点を詳しく紹介します。 もしこの記事が参考になれば記事末尾から活動を支援頂けると嬉しいです。

ピックアップ

個人的に注目する変更点を「ピックアップ」としてまとめました。 全ての変更点を網羅したリストは変更点リストをご覧ください。

OptionやResultの値をのぞき見られるようになった

Option::inspectResult::inspectResult::inspect_errにより、値を消費せず中の値をのぞき見ることができるようになりました。

これまで、OptionResultの中の値を見るためにはif letなどで束縛したり無意味なmapを使ったりしていました。 今回追加されたinspect{,_err}により簡単にできるようになりました。 これはprintln!を一行で仕込めるためデバッグ用にとても便利です。

// デバッグ用にSomeの時だけ中を見たい
fn without_inspect(x: Option<String>) -> String {
    // これまでは無意味なmapを使ったりしていた
    x
        .map(|x| {
            println!("{x}");
            x
        })
        .map(|x| format!("foo: {x}"))
        .unwrap_or_else(|| "hoge".to_string())
}

fn with_inspect(x: Option<String>) -> String {
    // Rust 1.76からはinspectにより簡単に書ける
    x
        .inspect(|x| eprintln!("{x}"))
        .map(|x| format!("bar: {x}"))
        .unwrap_or_else(|| "fuga".to_string())
}

最近のrust-analyzer

最近rust-analyzerに入った変更の中から個人的に気になったものをピックアップしました。

二重if文をひとつにまとめられるようになった

2024-01-15 (v0.3.1807)での変更です。

if文が二重になっているとき、1つ目のif文にカーソルがある際のコードアクションでif文を1つにまとめられるようになりました。 リファクタリングの時に便利ですね。

二重if文のコードアクションを表示した様子
二重if文のコードアクションを表示した様子

リテラルを別の表現で見られるようになった

2024-01-22 (v0.3.1815)での変更です。

文字列や数値リテラルでのかざし(hover)ヒントで、その値の別表現を見ることができるようになりました。

文字列の場合はエスケープ等が展開された実際の値が、整数の場合は10進・16進・2進での表現が、 小数の場合は実際の精度に合わせた値と16進表現が確認できます。

各リテラルのかざしヒントを表示した様子
リテラルのかざしヒントを表示した様子

安定化されたAPIのドキュメント

安定化されたAPIのドキュメントを独自に訳して紹介します。リストだけ見たい方は安定化されたAPIをご覧ください。

Arc::unwrap_or_clone

原典

    #[inline]
    #[stable(feature = "arc_unwrap_or_clone", since = "1.76.0")]
    pub fn unwrap_or_clone(this: Self) -> T
    { /* 実装は省略 */ }
}

Tに対する参照が1つだけの場合、それを展開する。そうでない場合、Tを複製して返す。

arc_tが型Ac<T>のとき、この関数は(*arc_t).clone()と等価であるが、可能な限り内部の値を複製しない。

サンプル

let inner = String::from("test");
let ptr = inner.as_ptr();

let arc = Arc::new(inner);
let inner = Arc::unwrap_or_clone(arc);
// 内部の値は複製されない
assert!(ptr::eq(ptr, inner.as_ptr()));

let arc = Arc::new(inner);
let arc2 = arc.clone();
let inner = Arc::unwrap_or_clone(arc);
// 2つの参照があるため内部の値の複製が必要
assert!(!ptr::eq(ptr, inner.as_ptr()));
// `arc2`は最後の参照なので、展開することで元の`String`が得られる
let inner = Arc::unwrap_or_clone(arc2);
assert!(ptr::eq(ptr, inner.as_ptr()));
use std::{ptr, sync::Arc};
let inner = String::from("test");
let ptr = inner.as_ptr();

let arc = Arc::new(inner);
let inner = Arc::unwrap_or_clone(arc);
// 内部の値は複製されない
assert!(ptr::eq(ptr, inner.as_ptr()));

let arc = Arc::new(inner);
let arc2 = arc.clone();
let inner = Arc::unwrap_or_clone(arc);
// 2つの参照があるため内部の値の複製が必要
assert!(!ptr::eq(ptr, inner.as_ptr()));
// `arc2`は最後の参照なので、展開することで元の`String`が得られる
let inner = Arc::unwrap_or_clone(arc2);
assert!(ptr::eq(ptr, inner.as_ptr()));

Rc::unwrap_or_clone

原典

    #[inline]
    #[stable(feature = "arc_unwrap_or_clone", since = "1.76.0")]
    pub fn unwrap_or_clone(this: Self) -> T
    { /* 実装は省略 */ }
}

Tに対する参照が1つだけの場合、それを展開する。そうでない場合、Tを複製して返す。

rc_tが型Rc<T>のとき、この関数は(*rc_t).clone()と等価であるが、可能な限り内部の値を複製しない。

サンプル

let inner = String::from("test");
let ptr = inner.as_ptr();

let rc = Rc::new(inner);
let inner = Rc::unwrap_or_clone(rc);
// 内部の値は複製されない
assert!(ptr::eq(ptr, inner.as_ptr()));

let rc = Rc::new(inner);
let rc2 = rc.clone();
let inner = Rc::unwrap_or_clone(rc);
// 2つの参照があるため内部の値の複製が必要
assert!(!ptr::eq(ptr, inner.as_ptr()));
// `rc2`は最後の参照なので、展開することで元の`String`が得られる
let inner = Rc::unwrap_or_clone(rc2);
assert!(ptr::eq(ptr, inner.as_ptr()));
use std::{ptr, rc::Rc};
let inner = String::from("test");
let ptr = inner.as_ptr();

let rc = Rc::new(inner);
let inner = Rc::unwrap_or_clone(rc);
// 内部の値は複製されない
assert!(ptr::eq(ptr, inner.as_ptr()));

let rc = Rc::new(inner);
let rc2 = rc.clone();
let inner = Rc::unwrap_or_clone(rc);
// 2つの参照があるため内部の値の複製が必要
assert!(!ptr::eq(ptr, inner.as_ptr()));
// `rc2`は最後の参照なので、展開することで元の`String`が得られる
let inner = Rc::unwrap_or_clone(rc2);
assert!(ptr::eq(ptr, inner.as_ptr()));

Result::inspect

原典

    #[inline]
    #[stable(feature = "result_option_inspect", since = "1.76.0")]
    pub fn inspect<F: FnOnce(&T)>(self, f: F) -> Self
    { /* 実装は省略 */ }
}

(値がOkの場合に)内部の値への参照を渡してクロージャを呼び出す。

サンプル

let x: u8 = "4"
    .parse::<u8>()
    .inspect(|x| println!("元の値:{x}"))
    .map(|x| x.pow(3))
    .expect("数値をパースできない");

Result::inspect_err

原典

    #[inline]
    #[stable(feature = "result_option_inspect", since = "1.76.0")]
    pub fn inspect_err<F: FnOnce(&E)>(self, f: F) -> Self
    { /* 実装は省略 */ }
}

(値がErrの場合に)内部の値への参照を渡してクロージャを呼び出す。

サンプル

use std::{fs, io};

fn read() -> io::Result<String> {
    fs::read_to_string("address.txt")
        .inspect_err(|e| eprintln!("ファイル読み込みに失敗:{e}"))
}

Option::inspect

原典

    #[inline]
    #[stable(feature = "result_option_inspect", since = "1.76.0")]
    pub fn inspect<F: FnOnce(&T)>(self, f: F) -> Self
    { /* 実装は省略 */ }
}

(値がSomeの場合に)内部の値への参照を渡してクロージャを呼び出す。

サンプル

let v = vec![1, 2, 3, 4, 5];

// "値:4"を出力
let x: Option<&usize> = v.get(3).inspect(|x| println!("値:{x}"));

// 何も出力しない
let x: Option<&usize> = v.get(5).inspect(|x| println!("値:{x}"));

type_name_of_val

原典

#[must_use]
#[stable(feature = "type_name_of_val", since = "1.76.0")]
#[rustc_const_unstable(feature = "const_type_name", issue = "63084")]
pub const fn type_name_of_val<T: ?Sized>(_val: &T) -> &'static str
{ /* 実装は省略 */ }

指す値の型名を文字列スライスとして返す。

これはtype_name::<T>()と同じだが、変数の型を簡単に得られない場合に使用できる。

メモ

type_nameと同様これは診断用でしかなく、正確な出力が保証されるものではない。 最善努力ベストエフォートで結果を提供するものの、出力はコンパイラのバージョン間で変更される可能性がある。

つまり、デバッグ用であることからプログラムの動作に影響を与えるような出力の使用は避けられたい。 詳細はtype_nameを参照のこと。

なお、この関数はトレイトオブジェクトを解決しない。 つまりtype_name_of_val(&7u32 as &dyn Debug)は、ここでは"u32"ではなく"dyn Debug"を返すだろう。

サンプル

既定の整数型、浮動小数点数型を表示する。

use std::any::type_name_of_val;

let s = "hoge";
let x: i32 = 1;
let y: f32 = 1.0;

assert!(type_name_of_val(&s).contains("str"));
assert!(type_name_of_val(&x).contains("i32"));
assert!(type_name_of_val(&y).contains("f32"));

ptr::from_mut

原典

#[inline(always)]
#[must_use]
#[stable(feature = "ptr_from_ref", since = "1.76.0")]
#[rustc_const_stable(feature = "ptr_from_ref", since = "1.76.0")]
#[rustc_allow_const_fn_unstable(const_mut_refs)]
#[rustc_never_returns_null_ptr]
pub const fn from_mut<T: ?Sized>(r: &mut T) -> *mut T
{ /* 実装は省略 */ }

可変参照を生のポインタに変換する。

これはr as *mut Tと同等だが、型や可変性を変えない分、 特にコードがリファクタリングされた場合などに幾分か安全である。

ptr::from_ref

原典

#[inline(always)]
#[must_use]
#[stable(feature = "ptr_from_ref", since = "1.76.0")]
#[rustc_const_stable(feature = "ptr_from_ref", since = "1.76.0")]
#[rustc_never_returns_null_ptr]
#[rustc_diagnostic_item = "ptr_from_ref"]
pub const fn from_ref<T: ?Sized>(r: &T) -> *const T
{ /* 実装は省略 */ }

参照を名前のポインタに変換する。

これはr as *const Tと同等だが、型や可変性を変えない分、 特にコードがリファクタリングされた場合などに幾分か安全である。

ptr::addr_eq

原典

#[stable(feature = "ptr_addr_eq", since = "1.76.0")]
#[inline(always)]
#[must_use = "pointer comparison produces a value"]
pub fn addr_eq<T: ?Sized, U: ?Sized>(p: *const T, q: *const U) -> bool
{ /* 実装は省略 */ }

2つのポインタのアドレスが等しいかどうかを、太いfatポインタのメタデータを無視して比較する。

引数が同じ型の薄いthinポインタである場合、eqと同義である。

サンプル

use std::ptr;

let whole: &[i32; 3] = &[1, 2, 3];
let first: &i32 = &whole[0];

assert!(ptr::addr_eq(whole, first));
assert!(!ptr::eq::<dyn std::fmt::Debug>(whole, first));

変更点リスト

言語

コンパイラ

Rustのティア付けされたプラットフォーム対応の詳細はPlatform Supportのページ(※訳注:英語)を参照

ライブラリ

安定化されたAPI

Cargo

Cargoのリリースメモ(※訳注:英語ページ)を参照。

Rustdoc

互換性メモ

関連リンク

さいごに

Rust 1.76での安定化が予定化されていたC文字列リテラルc"hoge")ですが、文字列中にNUL文字を含められるということで先送りされてしまいました。 その問題は既に修正されており、Rust 1.77で使えるようになる予定です。

次のリリースのRust 1.77は3/22(金)にリリースされる予定です。 Rust 1.77では前述の通りC文字列リテラルが使えるようになったり、スライスから配列を切り出せるようになったり、 非同期関数での再帰呼び出しが(条件付きで)できるようになったりする予定です。

ライセンス表記

  • この記事はApache 2/MITのデュアルライセンスで公開されている公式リリースノート及びドキュメントから翻訳・追記をしています
  • 冒頭の画像中にはRust公式サイトで配布されているロゴを使用しており、 このロゴはRust財団によってCC-BYの下で配布されています
  • 冒頭の画像中には標準C++財団のサイトで配布されているロゴを使用しており、このロゴは標準C++財団によって特定の条件下で配布されています
  • 冒頭の画像はいらすとやさんの画像を使っています。いつもありがとうございます