あずんひの日

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

Rust 1.77を早めに深掘り

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

ピックアップ

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

C文字列リテラルが使えるようになった

c"hoge"のような形で文字列を記述することで、末尾にNULL(\0)を含む文字列をstd::ffi::CStr型で得られるようになりました。

これまではCStr::from_bytes_with_nulを呼び出したりマクロで記述したりする必要があるなど少し面倒でした。

use std::ffi::CStr;

macro_rules! cstr {
    ($lit:literal) => {
        match CStr::from_bytes_with_nul($lit) {
            Ok(v) => v,
            Err(_) => panic!("invalid c-string literal"),
        }
    };
}

// X、Y、Zどれも同じ意味
const X: &CStr = match CStr::from_bytes_with_nul(b"hoge\0") {
    Ok(v) => v,
    Err(_) => panic!("invalid c-string literal"),
};
// 宣言型マクロでは\0を付ける必要がある(手続き型マクロなcstrクレートを使えば付ける必要はない)
const Y: &CStr = cstr!(b"hoge\0");
// Rust 1.77からは
const Z: &CStr = c"hoge";
// リテラル内に\0を含めることはできない
// const BAD: &CStr = c"\0";
//                      ^^
// error: null characters in C string literals are not supported

非同期関数を再帰呼び出しできるようになった

参照(主にはBox)を経由するという制限があるものの、非同期関数を再帰呼び出しすることができるようになりました。

async fn async_fibonacci(i: u32) -> u32 {
    if i == 0 || i == 1 {
        i
    } else {
        Box::pin(async_fibonacci(i - 1)).await 
          + Box::pin(async_fibonacci(i - 2)).await
    }
}

スライスの先頭や末尾を配列として切り出せるようになった

slice::{first,last}_chunk{,_mut}により、スライス&[T]から先頭や末尾のN要素を&[T; N]として切り出せるようになりました。 要素数が足りない場合はNoneが返るためパニックする心配もなく便利です。

また後続(先行)するスライスを得るためのslice::split_{first,last}_chunk{,_mut}もあります。

/// `slice`の先頭4バイトからビッグエンディアンで`u32`を読み込む。長さが足りない場合は`None`を返す。
fn read_u32(slice: &[u8]) -> Option<u32> {
    let bytes = slice.first_chunk()?;
    // Rust 1.76までは少し面倒な書き方が必要だった
    // let bytes: &[u8; 4] = slice.get(..std::mem::size_of::<u32>())?.try_into().unwrap();

    Some(u32::from_be_bytes(*bytes))
}

assert_eq!(read_u32(&[0x01]), None);
assert_eq!(read_u32(&[0x01, 0x02, 0x03, 0x04, 0x05]), Some(0x01020304));

フィールドの相対位置を取得できるようになった

std::mem::offset_of!マクロにより、構造体の先頭からフィールドまでのバイト数が分かるようになりました。

ただし、#[repr(C)]ではない構造体の数値は変わる可能性があることに注意が必要です。

#[repr(C)]
struct ReprC {
    x: u8,
    y: u8,
    z: u32,
}

struct Rust {
    x: u8,
    y: u8,
    z: u32,

    r: ReprC,
}

fn main() {
    // 数値は変わらない(アーキテクチャ等では変わる)
    assert_eq!(0, std::mem::offset_of!(ReprC, x));
    assert_eq!(1, std::mem::offset_of!(ReprC, y));
    assert_eq!(4, std::mem::offset_of!(ReprC, z));

    // 執筆時点のRust Playgroundでは4、5、0が表示されるが、今後この数値は変わる可能性がある
    println!("{}", std::mem::offset_of!(Rust, x));
    println!("{}", std::mem::offset_of!(Rust, y));
    println!("{}", std::mem::offset_of!(Rust, z));

    // 現時点では入れ子での使用はできない
    // println!("{}", std::mem::offset_of!(Rust, r.x));
}

最近のrust-analyzer

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

代入文からlet-elseに変換できるようになった

2024-02-12での変更です。

OptionResultなどTryトレイトを実装している型を代入する文があるとき、その部分でlet-else文に変換することができるようになりました。

一旦代入する文を書いておくことでSomeなどの記述を省略することができるので便利そうです。

代入文をlet-elseに変換する様子
代入文をlet-elseに変換する様子

use文の名称変更では別名を作るようになった

2024-02-26での変更です。

use文でインポートしている識別子をリネームすると、その識別子の定義を変えるのではなく、use文中でasを用いた別名が作られるようになりました。 これはあくまでuse文でのリネームだけであり、使用部分でリネームをすると定義が変わります。

mod hoge {
    pub struct X;
}

use hoge::X;
//        ^
// このXをYにリネームすると
// use hoge::X as Y;
// となる

type Hoge = X;
//          ^
// このXをYにリネームするとmod内の定義まで変わる

https://github.com/rust-lang/rust-analyzer/pull/16489

トレイトのかざしヒントに関連アイテムが表示されるようになった

2024-03-11での変更です。

トレイト上でかざし(hover)ヒントを表示させるときに設定した数だけトレイトの関連アイテムが表示されるようになりました。

既定では無効化されています。 rust-analyzer.hover.show.traitAssocItemsに整数を設定することで、その数だけアイテムを表示させることが出来ます。

Iteratorの関連アイテムを5つ表示させた様子
Iteratorの関連アイテムを5つ表示させた様子

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

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

array::each_ref

原典

    #[stable(feature = "array_methods", since = "1.77.0")]
    pub fn each_ref(&self) -> [&T; N]
    { /* 実装は省略 */ }
}

各要素を借用し、selfと同じ大きさである参照の配列を返す。

サンプル

let floats = [3.1, 2.7, -1.0];
let float_refs: [&f64; 3] = floats.each_ref();
assert_eq!(float_refs, [&3.1, &2.7, &-1.0]);

このメソッドは、mapのようなメソッドと組み合わせると特に便利である。 この場合、元の要素が[Copy]でない場合に元々の配列がムーブすることを回避できる。

let strings = ["Ferris".to_string(), "♥".to_string(), "Rust".to_string()];
let is_ascii = strings.each_ref().map(|s| s.is_ascii());
assert_eq!(is_ascii, [true, false, true]);

// まだ元々の配列を使用できる。ムーブされていない
assert_eq!(strings.len(), 3);

array::each_mut

原典

    #[stable(feature = "array_methods", since = "1.77.0")]
    pub fn each_mut(&mut self) -> [&mut T; N]
    { /* 実装は省略 */ }
}

各要素を可変借用し、selfと同じ大きさである参照の配列を返す。

サンプル

let mut floats = [3.1, 2.7, -1.0];
let float_refs: [&mut f64; 3] = floats.each_mut();
*float_refs[0] = 0.0;
assert_eq!(float_refs, [&mut 0.0, &mut 2.7, &mut -1.0]);
assert_eq!(floats, [0.0, 2.7, -1.0]);

float::round_ties_even

原典

impl f32 {
    #[rustc_allow_incoherent_impl]
    #[must_use = "method returns a new number and does not mutate the original value"]
    #[stable(feature = "round_ties_even", since = "1.77.0")]
    #[inline]
    pub fn round_ties_even(self) -> f32
    { /* 実装は省略 */ }
}

数値に最も近い整数を返す。中間の数値の場合、最下位の桁が偶数に丸められる。

サンプル

let f = 3.3_f32;
let g = -3.3_f32;
let h = 3.5_f32;
let i = 4.5_f32;

assert_eq!(f.round_ties_even(), 3.0);
assert_eq!(g.round_ties_even(), -3.0);
assert_eq!(h.round_ties_even(), 4.0);
assert_eq!(i.round_ties_even(), 4.0);

offset_of!

原典

#[cfg(not(bootstrap))]
#[stable(feature = "offset_of", since = "1.77.0")]
#[allow_internal_unstable(builtin_syntax, hint_must_use)]
pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?)
{ /* 実装は省略 */ }

指定された型において、先頭からフィールドまでのバイト単位でのオフセットへ展開する。

構造体、列挙型、共用体、及びタプルに対応する。

入れ子になったフィールドへのアクセスは使用できるが、配列のインデックスは使用できない。

列挙型のバリアントはフィールドであるかのように記述できるものの、 バリアントそれ自体にオフセットがあるわけではない。

ただし、安定版では単体のフィールド名のみに対応しており、列挙型への対応は封鎖されている。

可視性が考慮される。すなわちすべての型とフィールドは呼び出し元から可視でなくてはならない。

mod nested {
    #[repr(C)]
    pub struct Struct {
        private: u8,
    }
}

// assert_eq!(mem::offset_of!(nested::Struct, private), 0);
// ^^^ error[E0616]: field `private` of struct `Struct` is private

型の構造は、一般的に変更される可能性があり、またプラットフォーム固有 である。 もし構造が安定している必要がある場合は明示的なrepr属性の使用を検討されたい。

Rustは指定された型における指定されたフィールドのオフセットがプログラムの寿命内で変わらないことを保証する。 ただし、同じプログラムであっても別にコンパイルした場合には異なる構造となる可能性がある。 また単一プログラムの実行内であっても、同様ではあるが同一ではない型についての保証はない。 例えば・・・

struct Wrapper<T, U>(T, U);

type A = Wrapper<u8, u8>;
type B = Wrapper<u8, i8>;

// `u8`と`i8`は同じ構造であるとは言え、必ずしも同一とは言えない
// Not necessarily identical even though `u8` and `i8` have the same layout!
// assert!(mem::offset_of!(A, 1), mem::offset_of!(B, 1));

#[repr(transparent)]
struct U8(u8);

type C = Wrapper<u8, U8>;

// `u8`と`U8`は同じ構造であるとは言え、必ずしも同一とは言えない
// Not necessarily identical even though `u8` and `U8` have the same layout!
// assert!(mem::offset_of!(A, 1), mem::offset_of!(C, 1));

struct Empty<T>(core::marker::PhantomData<T>);

// `PhantomData`が常に同じ構造であるとは言え、必ずしも同一とは言えない
// assert!(mem::offset_of!(Empty<u8>, 0), mem::offset_of!(Empty<i8>, 0));

サンプル

#![feature(offset_of_enum, offset_of_nested)]

use std::mem;
#[repr(C)]
struct FieldStruct {
    first: u8,
    second: u16,
    third: u8
}

assert_eq!(mem::offset_of!(FieldStruct, first), 0);
assert_eq!(mem::offset_of!(FieldStruct, second), 2);
assert_eq!(mem::offset_of!(FieldStruct, third), 4);

#[repr(C)]
struct NestedA {
    b: NestedB
}

#[repr(C)]
struct NestedB(u8);

assert_eq!(mem::offset_of!(NestedA, b.0), 0);

#[repr(u8)]
enum Enum {
    A(u8, u16),
    B { one: u8, two: u16 },
}

assert_eq!(mem::offset_of!(Enum, A.0), 1);
assert_eq!(mem::offset_of!(Enum, B.two), 2);

assert_eq!(mem::offset_of!(Option<&u8>, Some.0), 0);
--> --> --> --> -->

slice::first_chunk

原典

impl<T> [T] {
    #[inline]
    #[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
    #[rustc_const_stable(feature = "slice_first_last_chunk", since = "1.77.0")]
    pub const fn first_chunk<const N: usize>(&self) -> Option<&[T; N]>
    { /* 実装は省略 */ }
}

スライスから最初のN個の項目への配列参照を返す。

もしスライスの長さがN未満の場合、このメソッドはNoneを返す。

サンプル

let u = [10, 40, 30];
assert_eq!(Some(&[10, 40]), u.first_chunk::<2>());

let v: &[i32] = &[10];
assert_eq!(None, v.first_chunk::<2>());

let w: &[i32] = &[];
assert_eq!(Some(&[]), w.first_chunk::<0>());

slice::first_chunk_mut

原典

impl<T> [T] {
    #[inline]
    #[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
    #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")]
    pub const fn first_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]>
    { /* 実装は省略 */ }
}

スライスから最初のN個の項目への可変な配列参照を返す。

もしスライスの長さがN未満の場合、このメソッドはNoneを返す。

サンプル

let x = &mut [0, 1, 2];

if let Some(first) = x.first_chunk_mut::<2>() {
    first[0] = 5;
    first[1] = 4;
}
assert_eq!(x, &[5, 4, 2]);

assert_eq!(None, x.first_chunk_mut::<4>());

slice::split_first_chunk

原典

impl<T> [T] {
    #[inline]
    #[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
    #[rustc_const_stable(feature = "slice_first_last_chunk", since = "1.77.0")]
    pub const fn split_first_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])>
    { /* 実装は省略 */ }
}

スライスから最初のN個の項目への配列参照と、残りのスライスを返す。

もしスライスの長さがN未満の場合、このメソッドはNoneを返す。

サンプル

let x = &[0, 1, 2];

if let Some((first, elements)) = x.split_first_chunk::<2>() {
    assert_eq!(first, &[0, 1]);
    assert_eq!(elements, &[2]);
}

assert_eq!(None, x.split_first_chunk::<4>());

slice::split_first_chunk_mut

原典

impl<T> [T] {
    #[inline]
    #[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
    #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")]
    pub const fn split_first_chunk_mut<const N: usize>(
        &mut self,
    ) -> Option<(&mut [T; N], &mut [T])>
    { /* 実装は省略 */ }
}

スライスから最初のN個の項目への可変な配列参照と、残りのスライスを返す。

もしスライスの長さがN未満の場合、このメソッドはNoneを返す。

サンプル

let x = &mut [0, 1, 2];

if let Some((first, elements)) = x.split_first_chunk_mut::<2>() {
    first[0] = 3;
    first[1] = 4;
    elements[0] = 5;
}
assert_eq!(x, &[3, 4, 5]);

assert_eq!(None, x.split_first_chunk_mut::<4>());

slice::last_chunk

原典

impl<T> [T] {
    #[inline]
    #[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
    #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")]
    pub const fn last_chunk<const N: usize>(&self) -> Option<&[T; N]>
    { /* 実装は省略 */ }
}

スライスから最後のN個の項目への配列参照を返す。

もしスライスの長さがN未満の場合、このメソッドはNoneを返す。

サンプル

let u = [10, 40, 30];
assert_eq!(Some(&[40, 30]), u.last_chunk::<2>());

let v: &[i32] = &[10];
assert_eq!(None, v.last_chunk::<2>());

let w: &[i32] = &[];
assert_eq!(Some(&[]), w.last_chunk::<0>());

slice::last_chunk_mut

原典

impl<T> [T] {
    #[inline]
    #[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
    #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")]
    pub const fn last_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]>
    { /* 実装は省略 */ }
}

スライスから最後のN個の項目への可変な配列参照を返す。

もしスライスの長さがN未満の場合、このメソッドはNoneを返す。

サンプル

let x = &mut [0, 1, 2];

if let Some(last) = x.last_chunk_mut::<2>() {
    last[0] = 10;
    last[1] = 20;
}
assert_eq!(x, &[0, 10, 20]);

assert_eq!(None, x.last_chunk_mut::<4>());

slice::split_last_chunk

原典

impl<T> [T] {
    #[inline]
    #[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
    #[rustc_const_stable(feature = "slice_first_last_chunk", since = "1.77.0")]
    pub const fn split_last_chunk<const N: usize>(&self) -> Option<(&[T], &[T; N])>
    { /* 実装は省略 */ }
}

スライスから最後のN個の項目への配列参照と、残りのスライスを返す。

もしスライスの長さがN未満の場合、このメソッドはNoneを返す。

サンプル

let x = &[0, 1, 2];

if let Some((elements, last)) = x.split_last_chunk::<2>() {
    assert_eq!(elements, &[0]);
    assert_eq!(last, &[1, 2]);
}

assert_eq!(None, x.split_last_chunk::<4>());

slice::split_last_chunk_mut

原典

impl<T> [T] {
    #[inline]
    #[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
    #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")]
    pub const fn split_last_chunk_mut<const N: usize>(
        &mut self,
    ) -> Option<(&mut [T], &mut [T; N])>
    { /* 実装は省略 */ }
}

スライスから最後のN個の項目への可変な配列参照と、残りのスライスを返す。

もしスライスの長さがN未満の場合、このメソッドはNoneを返す。

サンプル

let x = &mut [0, 1, 2];

if let Some((elements, last)) = x.split_last_chunk_mut::<2>() {
    last[0] = 3;
    last[1] = 4;
    elements[0] = 5;
}
assert_eq!(x, &[5, 3, 4]);

assert_eq!(None, x.split_last_chunk_mut::<4>());

slice::chunk_by

原典

impl<T> [T] {
    #[stable(feature = "slice_group_by", since = "1.77.0")]
    #[inline]
    pub fn chunk_by<F>(&self, pred: F) -> ChunkBy<'_, T, F>
    where
        F: FnMut(&T, &T) -> bool,
    { /* 実装は省略 */ }
}

スライスから述語関数を使用して要素を区切り、重複しない要素の連続部分を生成するイテレータを返す。

述語は連続する要素のペアごとに呼び出される。つまり、slice[0]slice[1]、その後にslice[1]slice[2]、 その後も同じように続く。

サンプル

let slice = &[1, 1, 1, 3, 3, 2, 2, 2];

let mut iter = slice.chunk_by(|a, b| a == b);

assert_eq!(iter.next(), Some(&[1, 1, 1][..]));
assert_eq!(iter.next(), Some(&[3, 3][..]));
assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
assert_eq!(iter.next(), None);

このメソッドはソートされた部分スライスを抽出するために使用できる。

let slice = &[1, 1, 2, 3, 2, 3, 2, 3, 4];

let mut iter = slice.chunk_by(|a, b| a <= b);

assert_eq!(iter.next(), Some(&[1, 1, 2, 3][..]));
assert_eq!(iter.next(), Some(&[2, 3][..]));
assert_eq!(iter.next(), Some(&[2, 3, 4][..]));
assert_eq!(iter.next(), None);

slice::chunk_by_mut

原典

impl<T> [T] {
    #[stable(feature = "slice_group_by", since = "1.77.0")]
    #[inline]
    pub fn chunk_by_mut<F>(&mut self, pred: F) -> ChunkByMut<'_, T, F>
    where
        F: FnMut(&T, &T) -> bool,
    { /* 実装は省略 */ }
}

スライスから述語関数を使用して要素を区切り、重複しない要素の可変な連続部分を生成するイテレータを返す。

述語は連続する要素のペアごとに呼び出される。つまり、slice[0]slice[1]、その後にslice[1]slice[2]、 その後も同じように続く。

サンプル

let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2];

let mut iter = slice.chunk_by_mut(|a, b| a == b);

assert_eq!(iter.next(), Some(&mut [1, 1, 1][..]));
assert_eq!(iter.next(), Some(&mut [3, 3][..]));
assert_eq!(iter.next(), Some(&mut [2, 2, 2][..]));
assert_eq!(iter.next(), None);

このメソッドはソートされた部分スライスを抽出するために使用できる。

let slice = &mut [1, 1, 2, 3, 2, 3, 2, 3, 4];

let mut iter = slice.chunk_by_mut(|a, b| a <= b);

assert_eq!(iter.next(), Some(&mut [1, 1, 2, 3][..]));
assert_eq!(iter.next(), Some(&mut [2, 3][..]));
assert_eq!(iter.next(), Some(&mut [2, 3, 4][..]));
assert_eq!(iter.next(), None);

File::create_new

原典

impl File {
    #[stable(feature = "file_create_new", since = "1.77.0")]
    pub fn create_new<P: AsRef<Path>>(path: P) -> io::Result<File>
    { /* 実装は省略 */ }
}

読み書きモードでファイルを生成する。ファイルが既に存在する場合はエラーとなる。

この関数はファイルが存在しない場合にファイルを作成し、ファイルが存在する場合はエラーを返す。 この方法で呼び出しが成功した場合、返されるファイルは新しいものであることが保証される。

この選択肢は原子的であることから便利である。そうでない場合、ファイルが存在することを確認した上で新しいファイルを作成することになるが、 別のプロセスによってファイルが生成される可能性がある(TOCTOU競合条件・攻撃)。

これはFile::options().read(true).write(true).create_new(true).open(...)のように書くこともできる。

サンプル

use std::fs::File;
use std::io::Write;

fn main() -> std::io::Result<()> {
    let mut f = File::create_new("hoge.txt")?;
    f.write_all("Hello, world!".as_bytes())?;
    Ok(())
}

Mutex::clear_poision

原典

impl<T: ?Sized> Mutex<T> {
    #[inline]
    #[stable(feature = "mutex_unpoison", since = "1.77.0")]
    pub fn clear_poison(&self)
    { /* 実装は省略 */ }
}

ミューテックス中毒poison状態を解除する。

ミューテックスが中毒状態にある場合、この関数が呼ばれるまで中毒状態が維持される。これにより中毒状態から回復し、 また回復したと示すことができる。例えば値が既知の良好な値で上書きされる場合、ミューテックスを中毒解除することができる。 また、値を検査し一貫した状態であることを判断した上で中毒を解除することもできるだろう。

サンプル

use std::sync::{Arc, Mutex};
use std::thread;

let mutex = Arc::new(Mutex::new(0));
let c_mutex = Arc::clone(&mutex);

let _ = thread::spawn(move || {
    let _lock = c_mutex.lock().unwrap();
    panic!(); // ミューテックスが中毒状態になる
}).join();

assert_eq!(mutex.is_poisoned(), true);
let x = mutex.lock().unwrap_or_else(|mut e| {
    **e.get_mut() = 1;
    mutex.clear_poison();
    e.into_inner()
});
assert_eq!(mutex.is_poisoned(), false);
assert_eq!(*x, 1);

RwLock::clear_poision

原典

impl<T: ?Sized> RwLock<T> {
    #[inline]
    #[stable(feature = "mutex_unpoison", since = "1.77.0")]
    pub fn clear_poison(&self)
    { /* 実装は省略 */ }
}

ロックの中毒poison状態を解除する。

ロックが中毒状態にある場合、この関数が呼ばれるまで中毒状態が維持される。これにより中毒状態から回復し、 また回復したと示すことができる。例えば値が既知の良好な値で上書きされる場合、ロックを中毒解除することができる。 また、値を検査し一貫した状態であることを判断した上で中毒を解除することもできるだろう。

サンプル

use std::sync::{Arc, RwLock};
use std::thread;

let lock = Arc::new(RwLock::new(0));
let c_lock = Arc::clone(&lock);

let _ = thread::spawn(move || {
    let _lock = c_lock.write().unwrap();
    panic!(); // ロックが中毒状態になる
}).join();

assert_eq!(lock.is_poisoned(), true);
let guard = lock.write().unwrap_or_else(|mut e| {
    **e.get_mut() = 1;
    lock.clear_poison();
    e.into_inner()
});
assert_eq!(lock.is_poisoned(), false);
assert_eq!(*guard, 1);

変更点リスト

言語

コンパイラ

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

ライブラリ

安定化されたAPI

Cargo

Rustdoc

その他

内部の変更

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

関連リンク

さいごに

次のリリースのRust 1.78は5/3(金)にリリースされる予定です。 Rust 1.78ではトレイトが実装されていない際のエラーメッセージをカスタマイズできるようになる予定です。

ライセンス表記

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