あずんひの日

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

Rust 1.69を早めに深掘り

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

ピックアップ

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

rustdocで検索する際にマクロだけ検索できるようになった

https://doc.rust-lang.org/std/https://docs.rs/などで項目を検索する際、 末尾に!を付けることでマクロだけを対象に検索することができるようになりました。

マクロ名は完全でなくても良いため、pr!という検索語句print!println!eprintln!などが引っ掛かります。

NULLを途中に含むスライスからでもCStrを直接生成できるようになった

C FFIとやり取りする際に便利なCStrですが、CStr::from_bytes_with_nulではスライス末尾にのみNULLが許容されており、 スライスの途中にNULLが入る場合には使えないため事前に自分でNULLを検索する必要があるなど少し面倒でした。

Rust 1.69から使えるようになったCStr::from_bytes_until_nulを使うことで、 途中にNULLを含むスライスであっても、最初に見つかったNULLを末尾としたCStrを生成できるようになります。

use std::ffi::CStr;

fn main() {
    let mut buf = [b'\0'; 255];
    unsafe {
        libc::sscanf(
            b"Rust 1.69\0".as_ptr().cast(),
            b"Rust %s\0".as_ptr().cast(),
            buf.as_mut_ptr(),
        );
    }
    // ここで`buf`はb"1.69\0\0\0..."となる

    // Rust 1.68まで
    // let idx = buf.iter().position(|&c| c == b'\0').unwrap();
    // let s = CStr::from_bytes_with_nul(&buf[..=idx]).unwrap();

    // Rust 1.69から
    let s = CStr::from_bytes_until_nul(&buf).unwrap();

    // "1.69"
    println!("{}", s.to_str().unwrap());
}

ソケットのアドレスが定数文脈で使えるようになった

IPアドレスとポート番号から構成されるSocketAddrの各種メソッドが定数文脈でも使えるようになり、 アドレスを定数として扱えるようになりました。

use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener};

// 定数として`SocketAddr`を生成する
const ADDR: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 80);
// `ADDR`からIPとポートを得る
const IP: IpAddr = ADDR.ip();
const PORT: u16 = ADDR.port();

fn main() {
    // 定数からTCPサーバーを起動
    let listener = TcpListener::bind(ADDR).unwrap();
    // ...
}

最近のrust-analyzer

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

クロージャの具体的な分類が分かるようになった

2023-04-17(v0.3.1481)での変更です。

クロージャを記述した際のヒントが、|u32, bool| -> Stringのような抽象的な型ではなく、 impl FnOnce(u32, bool) -> Stringのような、具体的な分類で分かるようになりました。

FnOnceでしかないクロージャであれば二度使うことができない、といったことがその場で分かって便利です。

FnOnce、FnMut、Fnとなるクロージャを並べた様子
FnOnce、FnMut、Fnとなるクロージャを並べた様子

構造体のフィールドにもシグネチャヘルプが出るようになった

2023-03-20(v0.3.1443)での変更です。

構造体のフィールドを記述する際、関数における引数と同じようにシグネチャヘルプが出るようになりました。

コンストラクタで戻り値を記述する際、いちいち定義を確認しに戻る必要がないため便利です。

Neovimにray-x/lsp_signature.nvimを導入した様子
Neovimにray-x/lsp_signature.nvimを導入した様子

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

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

CStr::from_bytes_until_nul

原典

impl CStr {
    #[rustc_allow_const_fn_unstable(const_slice_index)]
    #[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")]
    #[rustc_const_stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")]
    pub const fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError>
    { /* 実装は省略 */ }
}

バイトのスライスからC文字列のラッパーを生成する。

このメソッドは少なくとも1つのNULLバイトを含むバイトスライスからCStrを生成する。 呼び出し元はどこにNULLバイトがあるか知っている必要は無く、指定する必要も無い。

最初の文字がNULL文字だった場合、このメソッドは空のCStrを返す。 複数のNULL文字がある場合、CStrは最初の文字で終端する。

スライスが最後のNULLバイトのみを含む場合、このメソッドはCStr::from_bytes_with_nulと同等である。

サンプル

use std::ffi::CStr;

let mut buffer = [0u8; 16];
unsafe {
    // ここでバッファに文字列を書き込む安全でないC関数を呼び出しても良い
    let buf_ptr = buffer.as_mut_ptr();
    buf_ptr.write_bytes(b'A', 8);
}
// バッファからNULLで終端するC文字列を取り出そうとする
let c_str = CStr::from_bytes_until_nul(&buffer[..]).unwrap();
assert_eq!(c_str.to_str().unwrap(), "AAAAAAAA");

core::ffi::FromBytesUntilNulError

原典

#[derive(Clone, PartialEq, Eq, Debug)]
#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")]
pub struct FromBytesUntilNulError(_);

NULLバイトがなかったことを示すエラー。

CStrを生成するためのスライスには、スライスのどこかにNULLバイトが含まれる必要がある。

このエラーはCStr::from_bytes_until_nulメソッドによって生成される。

変更点リスト

言語

コンパイラ

ライブラリ

安定化されたAPI

以下のAPIが定数文脈で使えるようになった。

Cargo

rustdoc

互換性メモ

内部の変更

これらの変更がユーザーに直接利益をもたらすわけではないものの、コンパイラ及び周辺ツール内部では重要なパフォーマンス改善をもたらす。

関連リンク

さいごに

次のリリースのRust 1.70は6/2(金)にリリースされる予定です。 Rust 1.70では一度だけ値を設定できるOnceCellOnceLockが使えるようになるようです。

ライセンス表記

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