Skip to content
Mizar / みざー edited this page Apr 8, 2024 · 12 revisions

Rust 1.15.1 → Rust 1.42.0で追加された機能

はじめに

リンク集

Rustについて何かを調べたいときはまずはこちらから:

現在の各プラットフォームで使えるRust

fn main() { let dyn = 42; }fn main() { let _ = matches!(1, 1); }等のコードを提出することでversionとeditionを割り出しています。

プラットフォーム version edition opt-level target_pointer_width 外部クレート これら(←)の情報が載っているページ 最後に調べた日
AIZU ONLINE JUDGE 1.41.1 2015 ? 64 0個 あり 2021-06-26
AtCoder 1.42.0 2018 3 64 39個 あり 2021-06-26
Codeforces 1.49.0 2018 ? 32 0個 なし 2021-06-26
CodinGame 1.51.0 2018 ? 64 6個 あり 2021-06-26
Library-Checker 1.71.1 2021 3 64 0個 あり *1,*2,*3 2024-04-08
yukicoder 1.68.1 2021 3 64 0個 あり 2023-04-18
TODO: HackerRank, CS Academyその他

2015 editionであることによる不利益

以上の情報を割り出す方法

  • version

    このページにある機能を使ってみる。

    fn main() {
        matches!((), ()); // 1.42で追加
    }
  • edition

    2018 editionのみ強キーワードである単語がいくつかあるので、それを使う。

    fn main() {
        // 2018 editionでは`dyn`は強キーワードであるため、コンパイルエラー
        let dyn = 42;
    }
  • opt-level

    コードの実行だけで割り出すのは多分辛い。 コンパイルオプションがそのまま公開されているならそれを読む。

    少なくとも2はあると思われる。

  • target_pointer_width

    cfg attributeを使う。

    // 違う場合は`main`が消滅し、コンパイルエラー
    #[cfg(target_pointer_width = "期待するtarget_pointer_width")]
    fn main() {}
  • target_arch

    #[cfg(target_arch = "期待するtarget_arch")]
    fn main() {}

この記事で紹介しない機能

TODO

  • unsafeなもの (unchecked_系とかPinとか)
  • std::sync
  • Result絡み
  • 非ASCII文字列の取り扱いに関わるもの
  • 実装の置き換えによるパフォーマンス向上(キリが無いので)。ただし遅くて有名だったHash{Set, Map}とかには触れる
  • stdのみを使っていれば関係の無い機能
  • 1.15.1より後に安定化されたアイテムに追加された機能、または1.15.1より後に安定化された機能自体の更新(可能な限りまとめる)

言語仕様

structの初期化とパターンマッチでSelfが使えるようになった (more_struct_aliases, self_struct_ctor)

Minimum Rust Version: 1.16 Minimum Rust Version: 1.32

enumについても下記で紹介するtype_alias_enum_variantsで可能になります。

 struct Foo {
     param: i32,
 }

 impl Foo {
     fn new(param: i32) -> Self {
-        Foo { param }
+        Self { param }
     }

     fn perform(self) {
         match self {
-            Foo { .. } => {}
+            Self { .. } => {}
         }
     }
 }
 pub struct Foo(i32);

 impl Foo {
     pub fn new(val: i32) -> Self {
-        Foo(val)
+        Self(val)
     }

     pub fn perform(self) {
         match self {
-            Foo(_) => {}
+            Self(_) => {}
         }
     }
 }

Minimum Rust Version: 1.17

 let param = 42;
-let _ = Struct { param: param };
+let _ = Struct { param };

 struct Struct {
     param: i32,
 }

型の定義やimpl内でSelfが使えるようになった (allow_self_in_where_clauses, self_in_typedefs)

Minimum Rust Version: 1.16 Minimum Rust Version: 1.17 Minimum Rust Version: 1.32

struct Foo;

impl AsRef<Self> for Foo {
    fn as_ref(&self) -> &Foo {
        self
    }
}
use std::any::Any;

enum Cell<T>
where
    Self: Any,
{
    Nil,
    Cons(T, Box<Self>),
}

Minimum Rust Version: 1.17

-static S: &'static str = "foo";
+static S: &str = "foo";

Minimum Rust Version: 1.17

型境界にBorrowが使われているHashMapBTreeMap等にはあまり影響がありませんが、自分で実装した型にimpl Index<&'_ str> for ..のように書いた場合に自動的に&Stringも受け取れるようになります。

 use std::collections::HashMap;
 use std::ops::Index;

 let counter: ByteStringCounter = unimplemented!();
 let s: Vec<u8> = unimplemented!();

-let _: usize = counter[&s[..]];
+let _: usize = counter[&s];

 struct ByteStringCounter(HashMap<Vec<u8>, usize>);

 impl Index<&'_ [u8]> for ByteStringCounter {
     type Output = usize;

     fn index(&self, index: &'_ [u8]) -> &usize {
         self.0.get(index).unwrap_or(&0)
     }
 }

10.7. Type coercions - The Rust Reference

Minimum Rust Version: 1.17

static FOO: Foo<'_> = Foo { text: TEXT };
static TEXT: &str = "text";

struct Foo<'a> {
    text: &'a str,
}

Minimum Rust Version: 1.18

pubのかわりにpub(crate)pub(super)pub(in a::b::c)と書けるようになります。

ただしcrate rootから到達できるpubなアイテムはdead-codeのwarningが発生しないという利点があるので、この機能の出番はあまり無いかもしれません。

pub(crate) fn hello() {
    println!("Hello!");
}

Minimum Rust Version: 1.18

TODO: どうbreakingか

macro_rules! foo {
    ($ty:ty) => {};
}

foo!(A + B + ?Sized);

Minimum Rust Version: 1.19

マクロで使えるかもしれません。

let point = Point2(1.0, 2.0);
let point = Point2 { 0: 1.0, 1: 2.0 };

struct Point2(f64, f64);

Minimum Rust Version: 1.19

こうしていたのが、

let outcome;
loop {
    do_something();
    if p() {
        outcome = 42;
        break;
    }
}

こうできるようになります。

let outcome = loop {
    do_something();
    if p() {
        break 42;
    }
};

ラベル付きでも可能です。

#[allow(clippy::never_loop)]
let _ = 'label: loop {
    loop {
        break 'label 42;
    }
};

Minimum Rust Version: 1.19

impl FnMut(..) -> _等ではなくfnを扱いたい場合(キャプチャを行なわずかつ実行時に実装が選ばれる等)、それらを名前を付けずに簡潔に書くことができます。

let p: bool = unimplemented!();

let _: fn(_) -> _ = if p {
    |x: u32| -> _ { x + 1 }
} else {
    |x: u32| -> _ { 2 * x }
};

ただ自動でfnになるという訳ではないので、明示的に型強制する必要があります。

10.7. Type coercions - The Rust Reference

Minimum Rust Version: 1.20

Associated Itemとしてconstが追加できるようになりました。

 trait ConstValue {
     type Value: Copy;
-    fn value() -> Self::Value;
+    const VALUE: Self::Value;
 }

Minimum Rust Version: 1.21

TODO: ちゃんと解説

let _: &'static i32 = &0;
let _: &'static Foo = &Foo;

struct Foo;

// `Drop`を実装すると駄目
//impl Drop for Foo {
//    fn drop(&mut self) {}
//}
 use std::{borrow::Borrow, collections::HashMap, hash::Hash, ops::Index};

 struct Counter<T>(HashMap<T, usize>);

 impl<T, Q> Index<&'_ Q> for Counter<T>
 where
     T: Eq + Hash + Borrow<Q>,
     Q: Eq + Hash,
 {
     type Output = usize;

     fn index(&self, key: &Q) -> &usize {
-        static ZERO: usize = 0;
-        self.0.get(key).unwrap_or(&ZERO)
+        self.0.get(key).unwrap_or(&0)
     }
 }

Minimum Rust Version: 1.21

turbofish付きのpathを受理するようになりました。

macro_rules! pass_path {
    ($_path:path) => {};
}

pass_path!(Vec::<i32>::new);

2.7. Paths - The Rust Reference

Minimum Rust Version: 1.22

例えばi32Add<&'_ i32>を実装していて、1 + &1のような書き方ができます。 イテレータのメソッドチェーンから&{integer}が出て来ることはよくありますが、*でdereferenceしなくても参照のまま四則演算の演算子を適用することができます。

let _: i32 = 1 + &1; // `<i32 as Add<&'_ i32>>::add`
let _: i32 = &1 + 1; // `<&'_ i32 as Add<i32>>::add`

しかし1.21まで整数型はAddAssign<&'_ Self>等を実装していませんでした。 1.22からはstd::num::Wrappingを含めて&'_ Selfに対しても複合代入演算子が適用できるようになります。

 for x in &xs {
-    acc += *x;
+    acc += x;
 }

Minimum Rust Version: 1.22

const FOO: Foo = Foo;

struct Foo;

impl Drop for Foo {
    fn drop(&mut self) {}
}

Minimum Rust Version: 1.23

以下の問題が修正されました。

Binary operator LHS type is fixed instead of being subtyped #45425

Minimum Rust Version: 1.25

このようにuse文を書けるようになります。

// rustfmtはこの量でも縦に引き伸ばす
use std::{
    collections::BinaryHeap,
    io::{self, Read as _},
};

ただ見た目はスッキリしますが書きやすいという訳ではないので、ライブラリをゆっくり書くときにこの書き方をすると良いでしょう。

3.2.4. Nested imports with use - The Edition Guide

Minimum Rust Version: 1.25

普通に書いた場合Rustfmtにより即座に消されますが、マクロでmatchアームを生成する場合役に立つかもしれません。

let dir: Direction = unimplemented!();

#[rustfmt::skip]
match dir {
    | Direction::Left
    | Direction::Right => println!("horizontal"),
    | Direction::Down
    | Direction::Up => println!("vertical"),
};

enum Direction {
    Left,
    Right,
    Down,
    Up,
}

Minimum Rust Version: 1.26

std::ops::{RangeInclusive, RangeToInclusive}とそれらのコンストラクタ..=が追加されました。

この2つは右側がinclusiveなRange, RangeToです。 これでl..r + 1とする必要はなくなります。

-for i in l..r + 1 {
+for i in l..=r {
-perform(&xs[..n + 1]);
+perform(&xs[..=n]);

またパターンマッチの...は非推奨になり、..=を使うようになりました。

 match x {
-    1...10 => f(x),
+    1..=10 => f(x),
     _ => {}
 }

Minimum Rust Version: 1.26

TODO

  1. 関数の引数のimpl trait
  2. 関数の返り値のimpl trait

1.は関数の型引数指定のシンタックスシュガーです。

このimpl Traitは匿名の型引数になります。

-fn foo<P: FnMut(i64) -> bool>(mut p: P) {
+fn foo(mut p: impl FnMut(i64) -> bool) {
     todo!();
 }

ただし<>内の方に書いたものを含め、turbofishによる型の指定ができなくなります。 特に明示的に書いた型引数が返り値に出現する場合等は避けたほうが良いでしょう。

2.はTODO

use std::{
    iter::{self, FromIterator},
    ops::{Index, IndexMut},
};

/// Counts ASCII characters.
///
/// # Examples
///
/// ```rust
/// use my_competitive_library::counter::AsciiCounter;
///
/// let counter = AsciiCounter::from_bytes("aaabbbccc");
///
/// assert_eq!(counter.elements().collect::<Vec<_>>(), b"aaabbbccc");
/// ```
pub struct AsciiCounter(Box<[usize; 256]>);

impl AsciiCounter {
    pub fn new() -> Self {
        Self(Box::new([0; 256]))
    }

    pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
        bytes.as_ref().iter().copied().collect()
    }

    pub fn elements<'a>(&'a self) -> impl Iterator<Item = u8> + 'a {
        (0u8..=127).flat_map(move |c| iter::repeat(c).take(self[c]))
    }
}

#[rustfmt::skip] impl Default for AsciiCounter { fn default() -> Self { Self::new() } }

#[rustfmt::skip] impl FromIterator<u8> for AsciiCounter { fn from_iter<I: IntoIterator<Item = u8>>(iter: I) -> Self { let mut this = Self::new(); for ch in iter { this[ch] += 1; } this } }

#[rustfmt::skip] impl Index<u8> for AsciiCounter { type Output = usize; fn index(&self, ch: u8) -> &usize { &self.0[usize::from(ch)] } }

#[rustfmt::skip] impl IndexMut<u8> for AsciiCounter { fn index_mut(&mut self, ch: u8) -> &mut usize { &mut self.0[usize::from(ch)] } }

Minimum Rust Version: 1.26

TODO

let x: &Option<i32> = unimplemented!();

if let Some(x) = x {
    let _: &i32 = x;
}

Minimum Rust Version: 1.26

i128u128が使えるようになりました。

#![allow(clippy::unreadable_literal)]

let x = 0u128;
let y = 0i128;

assert_eq!(u128::max_value(), 0xffffffffffffffffffffffffffffffff);
assert_eq!(i128::max_value(), 0x7fffffffffffffffffffffffffffffff);

main#[test]の型を()に加えResutl<(), impl Debug>にできるようになった (termination_trait, termination_trait_test)

Minimum Rust Version: 1.26 Minimum Rust Version: 1.28

mainとテスト関数の戻り値に()以下の型が使えるようになります。

  • !
  • ExitCode (unstable)
  • Result<(), E> (E: Debug)
  • Result<!, E> (E: Debug) (unstable)

使い道があるとしたら、標準入出力用のユーティリティに対するテスト関数の戻り値をio::Result<()>にすることくらいでしょうか。

Slice patterns (slice_patterns)

Minimum Rust Version: 1.26 Minimum Rust Version: 1.42

[T][T; N]の要素に対してパターンマッチができるようになりました。

ただし非Copyな要素をmove outすることはできないので注意しましょう。 またVec<T>String&strと同様に、明示的に&[T]にdereferenceする必要があります。

let xs: Vec<i32> = unimplemented!();

match *xs {
    [] => todo!(),
    [x] => todo!(),
    [x, y] => todo!(),
    _ => todo!(),
}

1.42では"subslice pattern"が使えるようになりました。 OCamlやHaskell、Scala等のx :: xs -> ...のような書き方ができるようになります。

fn merge<T: Copy + PartialOrd>(mut left: &[T], mut right: &[T]) -> Vec<T> {
    let mut acc = vec![];
    loop {
        match (left, right) {
            ([..], []) | ([], [..]) => {
                acc.extend(left.iter().chain(right).copied());
                break acc;
            }
            ([x, xs @ ..], [y, ..]) if x <= y => {
                acc.push(*x);
                left = xs;
            }
            ([_, ..], [y, ys @ ..]) => {
                acc.push(*y);
                right = ys;
            }
        }
    }
}

いくつかの単語がキーワードではなくなった

Minimum Rust Version: 1.27 Minimum Rust Version: 1.28

1.0以前に使われていて、今は使われていない単語が解放されました。

2.2. Keywords - The Rust Reference

Minimum Rust Version: 1.27

trait Traitについて&Trait, Box<Trait>等と書いていたのを&dyn Trait, Box<dyn Trait>と書けるようになり、また以前の書き方は非推奨になりました。

-let _: &Trait = unimplemented!();
+let _: &dyn Trait = unimplemented!();

-let _: Box<Trait> = unimplemented!();
+let _: Box<dyn Trait> = unimplemented!();

 trait Trait {}

attributeが適応できる場所が増えた (generic_param_attrs, param_attrs)

Minimum Rust Version: 1.27 Minimum Rust Version: 1.37 Minimum Rust Version: 1.39

TODO

macro_rules!にメタ変数のタイプとしてlifetime, vis, literalが追加された (macro_lifetime_matcher, macro_vis_matcher, macro_literal_matcher)

Minimum Rust Version: 1.28 Minimum Rust Version: 1.30 Minimum Rust Version: 1.32

TODO: ↓より良い例

 pub struct NotNanF64(f64);

 impl NotNanF64 {
     /// Constructs a new `NotNanF64`.
     ///
     /// # Panics
     ///
     /// Panics if `val` is NaN.
     pub fn checked(val: f64) -> Self {
         if val.is_nan() {
             panic!("NaN");
         }
         Self(val)
     }

     pub fn unchecked(val: f64) -> Self {
         Self(val)
     }
 }

 #[macro_export]
-macro_rules! notnanf64(($val:expr $(,)?) => ($crate::path::to::here::NotNanF64::checked($expr)));
+macro_rules! notnanf64(($val:literal $(,)?) => ($crate::path::to::here::NotNanF64::unchecked($val))); # NaNに対するリテラルは無い

Metavariables - 3.1. Macros By Example - The Edition Guide

Minimum Rust Version: 1.30

先頭にr#を付けることでキーワードを識別子に使うことができるようになります。

// このような入力を想定
//
// N
// A_1 A_2 ... A_N
// B_1 B_2 ... B_N
// C_1 C_2 ... C_N

static INPUT: &str = r#"5
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
"#;

let mut input = INPUT.split_ascii_whitespace();
macro_rules! read(
    ([$ty:tt; $len:expr]) => ((0..$len).map(|_| read!($ty)).collect::<Vec<_>>());
    ($ty:ty) => (input.next().unwrap().parse::<$ty>().unwrap());
);

let n = read!(usize);
let r#as = read!([u32; n]); // `as`はキーワード
let bs = read!([u32; n]);
let cs = read!([u32; n]);

assert_eq!(n, 5);
assert_eq!(r#as, &[1, 2, 3, 4, 5]);
assert_eq!(bs, &[6, 7, 8, 9, 10]);
assert_eq!(cs, &[11, 12, 13, 14, 15]);

2.3. Identifiers - The Rust Reference

絶対パスをcrate::foo::barの形式で書くようになった

Minimum Rust Version: 1.30

ローカルのアイテムを絶対パスで書くときにcrate::foo::barのように書けるようになります。

mod foo {
    pub fn bar() {
        // ...
    }
}

// old
use ::foo::bar;
// or
use foo::bar;

// new
use crate::foo::bar;

Announcing Rust 1.30 - Rust Blog

Rust 2018では絶対パスにcrate::が必須になります。

use std;が不要になった

Minimum Rust Version: 1.30

TODO

Minimum Rust Version: 1.30

外部クレートのマクロをuseできるようになったついでに、クレート内でのマクロの前方参照ができるようになりました。 このようにマクロをソースコードの下側で宣言できるようになります。

fn main() {
    foo!();
}

mod _macros {
    #[macro_export]
    macro_rules! foo(() => (println!("foo")));
}

3.10.2. Macro changes - The Edition Guide

Minimum Rust Version: 1.30

TODO

[breaking-change] Rust 2018

Minimum Rust Version: 1.31

TODO

TODO: cargo fix --edition

[breaking-change] NLL (Non-lexical lifetimes)

Minimum Rust Version: 1.31 for 2018 edition

Minimum Rust Version: 1.36 for 2015 edition

TODO

Rustは1.31.0をもって、borrow checkerが大幅に進化しました。 このおかげで以前よりもはるかに自由にコードが書けるようになりました。 この記事で紹介する範囲の中では最も大きな進歩と言えるでしょう。

どう嬉しいかを説明していきます。 このようなコードを見たことはないでしょうか?

TODO: <[_]>::split_at_mut<[_]>::swap_with_valueの例がちょうど良いのでは

let mut store = Store::new();

{
    let item = store.item_mut();
    if p(item) {
        modify(item);
    }
}

store.close();

このコードは、今のRustならこの{}を取り払ってもコンパイルエラーは生じません。

let mut store = Store::new();

let item = store.item_mut();
if p(item) {
    modify(item);
}

store.close();

しかし古いRustでは取り払うとエラーになります。

error[E0505]: cannot move out of `store` because it is borrowed
 --> ./src/bin/hage.rs:9:5
  |
4 |     let item = store.item_mut();
  |                ----- borrow of `store` occurs here
...
9 |     store.close();
  |     ^^^^^ move out of `store` occurs here

error: aborting due to previous error

TODO

TODO: Rust の借用検査は組込の複合代入演算子に対して特殊な動作を行う - 何とは言わない天然水飲みたさ

型の宣言内での&'a Tというパラメータの存在からT: 'aが推論されるようになった

Minimum Rust Version: 1.31

Tracking issue for RFC 2093: Infer T: 'x outlives requirements on structs · Issue #44493 · rust-lang/rust

struct Foo<'a, T> {
    reference: &'a T,
}

ここからT: 'aが推論されるようになります。 以前は明示的に指定する必要がありました。

$ rustc +1.30.1 --crate-type lib ./src/lib.rs
error[E0309]: the parameter type `T` may not live long enough
 --> ./src/lib.rs:2:5
  |
1 | struct Foo<'a, T> {
  |                - help: consider adding an explicit lifetime bound `T: 'a`...
2 |     reference: &'a T,
  |     ^^^^^^^^^^^^^^^^
  |
note: ...so that the reference type `&'a T` does not outlive the data it points at
 --> ./src/lib.rs:2:5
  |
2 |     reference: &'a T,
  |     ^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0309`.

$crate::

Minimum Rust Version: 1.31

TODO

pub mod modint {
    ///! ```
    ///! # use my_competitive_library::decl_modint;
    ///! decl_modint!(Zp, 1_000_000_007);
    ///! ```

    use std::marker::PhantomData;

    #[macro_export]
    macro_rules! decl_modint {
        ($name:ident, $modulus:literal) => {
            #[derive(Clone, Copy, PartialEq, Eq, Debug)]
            enum __Modulus {}

            impl $crate::modint::Modulus for __Modulus {
                const VALUE: u64 = $modulus;
            }

            type $name = $crate::modint::ModInt<__Modulus>;
        };
    }

    pub trait Modulus {
        const VALUE: u64;
    }

    #[derive(Clone, Copy, PartialEq, Eq, Debug)]
    pub struct ModInt<M: Modulus> {
        repr: u64,
        phantom: PhantomData<fn() -> M>,
    }
}

Macros with $crate:: prefix - 3.10.2. Macro changes - The Edition Guide

Minimum Rust Version: 1.31

TODO

#![deny(rust_2018_idioms)] // `elided-lifetimes-in-paths`を含む

use std::io::{self, BufWriter, StdoutLock, Write as _};

fn buf_print<F: FnOnce(&mut BufWriter<StdoutLock<'_>>)>(f: F) {
    let stdout = io::stdout();
    let mut stdout = BufWriter::new(stdout.lock());
    f(&mut stdout);
    stdout.flush().unwrap();
}

Minimum Rust Version: 1.31 Minimum Rust Version: 1.33

TODO: const fn化したstdの関数

Minimum Rust Version: 1.31

TODO

  • clippyrustcにとって既知のツールになった

renamed-and-removed-lints

Minimum Rust Version: 1.31

match $expr {
    $pattern => {}
}

match $expr {
    ($pattern) => {}
}

と書けるようになりました。(unused_parensとかには引っ掛ります)

Minimum Rust Version: 1.32 for 2018 edition

Minimum Rust Version: 1.37 for 2015 edition

*が0以上、+が1以上の繰り返しを指定するのに対し、?は「0回か1回」を指定します。

echo!(42);
echo!(42,); // trailing comma

#[macro_export]
macro_rules! echo {
    ($expr:expr $(,)?) => {
        ::std::println!("{}", $expr)
    };
}

Minimum Rust Version: 1.32

TODO

use Foo::A;

const FOO: Foo = A;

enum Foo {
    A,
    B,
}

Minimum Rust Version: 1.32

use std::marker::PhantomData;

const PHANTOM: PhantomData<()> = PhantomData;

match PhantomData {
    PHANTOM => {}
}

Minimum Rust Version: 1.33

 let x: u8 = unimplemented!();

 match x {
     0..=255 => unimplemented!(),
-    _ => unreachable!("should be exhaustive"),
 }

Minimum Rust Version: 1.33

if let Foo::A(_) | Foo::B(_) = unimplemented!() {
    todo!();
}

enum Foo {
    A(i32),
    B(i32),
    C(i32),
}

Minimum Rust Version: 1.33

let .. = ..;に置き換えられるif letloop { let .. = ..; .. }に置き換えられるwhile letは、1.32.0以前まではhard errorになっていましたが1.33.0以降warningに格下げされました。

マクロを書くときにirrefutableかどうかを気にせずに済むようになります。

#![allow(irrefutable_let_patterns)]

if let () = () {
    todo!();
} else {
    unreachable!();
}
#![allow(irrefutable_let_patterns)]

let mut state: State = unimplemented!();

while let State { count } = &mut state {
    if *count == 0 {
        break;
    }
    *count -= 1;
}

struct State {
    count: usize,
}

Minimum Rust Version: 1.33

Minimum Rust Version: 1.33

トレイトのメソッドだけ使いたいときに、as _としてインポートできます。

-use std::io::{self, Read};
+use std::io::{self, Read as _};

 let mut input = "".to_owned();
 io::stdin().read_to_string(&mut input).unwrap();

同名のトレイトのメソッドを使いたいときに変な名前を付けるといったハックが不要になります。

-use std::{fmt::Write as _FmtWrite, io::Write as _IoWrite};
+use std::{fmt::Write as _, io::Write as _};

メソッドのレシーバーに使える型が増えた

Minimum Rust Version: 1.33 Minimum Rust Version: 1.41

Rc, Arc, Pinがレシーバーとして使えるようになったほか、Box<Box<Self>>のようにネストできるようになりました。

use std::rc::Rc;
use std::sync::Arc;

struct Foo;

impl Foo {
    fn method(self: &mut Rc<Arc<Box<&Self>>>) {
        todo!();
    }
}

Minimum Rust Version: 1.34

TODO: ライブラリを書くときに使える..?

Minimum Rust Version: 1.35

参照で呼び出せるFn, FnMutとは違い、Box<dyn FnOnce(..) -> _>は作っても呼びだせませんでしたが直接FnOnceが実装されることでちゃんと使えるようになりました。

let p = || -> bool { unimplemented!() };

let f: Box<dyn FnOnce(_) -> _> = if p() {
    Box::new(|x| x + 1)
} else {
    Box::new(|x| 2 * x)
};
let _: i32 = f(42);

RustでCPS変換が簡単になったよという話 - κeenのHappy Hacκing Blog

Minimum Rust Version: 1.37

これにより、enumの値を書くのに型名の部分をSelfと書けるようになりました。

 enum Language {
     En,
     Ja,
 }

 impl Language {
     fn hello(self) {
         match self {
-            Language::En => println!("Hello"),
-            Language::Ja => println!("こんにちは"),
+            Self::En => println!("Hello"),
+            Self::Ja => println!("こんにちは"),
         }
     }
 }

Minimum Rust Version: 1.37

static_assert_size!等のマクロが書きやすくなります

const _: () = {
    let _: fn(usize, usize) -> usize = usize::saturating_sub;
};

Minimum Rust Version: 1.37

recursion_limitのデフォルト値は1.42.0時点で128になっています。

リリースノートやRust Blogには記されていませんが、128になったのは1.37.0の時点のようです。

macro_rules! rec {
    () => {};
    (_ $($rest:tt)*) => {
        rec!($($rest)*);
    }
}

rec!(
    // 127個
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
);

Minimum Rust Version: 1.39

1.38以前ではby move patternではifガードを使えませんでした。

todo!("なんか良い例");

Minimum Rust Version: 1.40

1.39以前ではコンストラクタ自体はconst内で使えましたが、値として扱うとconst fnではなくなっていました。

const _: () = {
    struct Foo(i32);

    let _ = { Foo }(42);
};

Minimum Rust Version: 1.40

Minimum Rust Version: 1.41

impl<A, B, C> ForeignTrait<LocalType> for ForeignType<A, B, C> {
    // ...
}

の形式でのimplが可能になります。 型引数無しなら以前から可能でした。

例にあるようなFromAsRefのほか、[T]Vec<T>Index<MyInt>を実装させるといったことができます。

標準ライブラリ

Minimum Rust Version: 1.16

TODO: 1.44の今はどうなんだろう?

Minimum Rust Version: 1.16

 use std::io::{BufWriter, StdoutLock, Write as _};

 let mut stdout: BufWriter<StdoutLock<'_>> = unimplemented!();

-writeln!(stdout, "").unwrap();
+writeln!(stdout).unwrap();
 use std::io::{BufWriter, StdoutLock, Write as _};

 let mut stdout: BufWriter<StdoutLock<'_>> = unimplemented!();

-macro_rules! println {
-    () => {
-        writeln!(stdout, "\n").unwrap()
-    };
-    ($($tt:tt)*) => {
-        writeln!(stdout, $($tt)*).unwrap()
-    };
-}
+macro_rules! println(($($tt:tt)*) => (writeln!(stdout, $($tt)*).unwrap()));

Minimum Rust Version: 1.16

Vec::truncateのようなことができます。

queue.truncate(1);

Minimum Rust Version: 1.16

Vec::resizeのようなことができます。

queue.resize(n, (0, 0));

Minimum Rust Version: 1.16

ある位置に&strを挿入できます。

acc.insert_str(i, s);

Minimum Rust Version: 1.16

回数制限のあるreplaceです。

assert_eq!(
    "aaa aaa aaa aaa".replacen("aaa", "bbb", 2),
    "bbb bbb aaa aaa",
);

Minimum Rust Version: 1.16 Minimum Rust Version: 1.40

Pythonのlist.__mul__のようなことができます。

assert_eq!("abc".repeat(2), "abcabc");
assert_eq!([1, 2].repeat(3), vec![1, 2, 1, 2, 1, 2]);
assert_eq!(b"abc".repeat(2), b"abcabc");

std::vec::Vec::{dedup_by, dedup_by_key}

Minimum Rust Version: 1.16

dedup_by, _by_key版です。

let mut xs = vec![3, 2, 4, 0, 5];
xs.dedup_by_key(|&mut x| x % 2);
assert_eq!(xs, &[3, 2, 5]);

Minimum Rust Version: 1.16

Vec::split_offString版です。

let mut left = "abcdef".to_owned();
let right = left.split_off(left.len() / 2);

assert_eq!(left, "abc");
assert_eq!(right, "def");

Minimum Rust Version: 1.17

「最後にキューがある状態かどうかが答え」という場合に使えるかもしれません。

use std::collections::VecDeque;

let queue: VecDeque<i32> = unimplemented!();
let x: i32 = unimplemented!();
let y: i32 = unimplemented!();
let z: i32 = unimplemented!();

let ans = queue == &[x, y, z];
println!("{}", if ans { "Yes" } else { "No" });
dbg!(queue == &[x, y, z]);

std::cmp::Ordering::{then, then_with}

Minimum Rust Version: 1.17

左側がLess, Greaterのときその左側を、Equalのときに右側を返す関数です。

 let heavy_calc = |_: i32| -> i64 { unimplemented!() };
 let x: i32 = unimplemented!();
 let y: i32 = unimplemented!();

-(x, heavy_calc(x)).cmp(&(y, heavy_calc(y)))
+x.cmp(&y).then_with(|| heavy_calc(x).cmp(&heavy_calc(y))) // `x != y`なら`heavy_calc`は呼ばれない

std::ops::{Bound, RangeBounds}

Minimum Rust Version: 1.17 Minimum Rust Version: 1.28

RangeBoundsa..ba.., ..b等に対するトレイトで、Boundはその左右の境界を表わす列挙型です。

「整数の範囲」を扱うライブラリを書くときに便利です。

-use std::ops::Range;
+use std::ops::{Bound, RangeBounds};

-fn perform(range: Range<usize>) {
-    let Range { start, end } = range;
+fn perform(range: impl RangeBounds<usize>) {
+    let start = match range.start_bound() {
+        Bound::Included(&start) => start,
+        Bound::Excluded(start) => start.saturating_sub(1),
+        Bound::Unbounded => 0,
+    };
+
+    let end = match range.end_bound() {
+        Bound::Included(end) => Some(end + 1),
+        Bound::Excluded(&end) => Some(end),
+        Bound::Unbounded => None,
+    };

     unimplemented!();
 }

range, range_mut

Minimum Rust Version: 1.17

指定したRangeBoundsに収まる値のイテレータを返します。

C++のstd::lower_boundstd::upper_bound(ただし欲しいのは値だけ)の代用としても使えます。

use std::collections::BTreeSet;

macro_rules! btreeset(($($val:expr),* $(,)?) => (vec![$($val),*].into_iter().collect::<BTreeSet<_>>()));

let set = btreeset!(10, 20, 30, 40, 50);

let range = set.range(25..).collect::<Vec<_>>();
assert_eq!(range, &[&30, &40, &50]);

TODO: これらのイテレータはサイズを保持していないっぽく、<_ as Iterator>::countIteratorのデフォルト実装が使われるっぽいのでC++のstd::partition_mapのように使おうとすると多分大惨事になる

Minimum Rust Version: 1.17

ラッパー型に付き物のinto_innerです。

use std::cell::Cell;

let x: i32 = unimplemented!();

let _: i32 = Cell::new(x).into_inner();

std::cell::{Cell::{swap, replace, take}, RefCell::{swap, replace}}

Minimum Rust Version: 1.17 Minimum Rust Version: 1.24

CellRefCellに対してstd::mem::{swap, replace, take}のようなことができます。

ただし1.44の現在RefCell::takeはまだありません。

use std::cell::Cell;

let x = Cell::new(1);
let y = Cell::new(2);

x.swap(&y);
assert_eq!(x.get(), 2);
assert_eq!(y.get(), 1);

let z = x.replace(3);
assert_eq!(x.get(), 3);
assert_eq!(y.get(), 1);
assert_eq!(z, 2);

let z = x.take();
assert_eq!(x.get(), 0);
assert_eq!(y.get(), 1);
assert_eq!(z, 3);

retain

Minimum Rust Version: 1.18 Minimum Rust Version: 1.26

Vec::retainのようにある条件を満たさない値を消します。

use std::collections::HashSet;

macro_rules! hashset(($($val:expr),* $(,)?) => (vec![$($val),*].into_iter().collect::<HashSet<_>>()));

let mut set = hashset!(1, 2, 3, 4, 5);
set.retain(|x| x % 2 == 0);
assert_eq!(set, hashset!(2, 4));

Minimum Rust Version: 1.18

BinaryHeap::peek_mutでpeekした要素をそのままpopできます。

「先頭の要素がある条件を満たしたらpop、そうでなければその要素に変更を加えてpopはしない」ということをやりたい場合にスマートな書きかたができるのですが、borrow checkerが前述したNLLに切り替わった今だと微妙かもしれません。

-use std::collections::BinaryHeap;
+use std::collections::{binary_heap::PeekMut, BinaryHeap};

 let p = |_: i32| -> bool { unimplemented!() };
 let modify = |_: &mut i32| unimplemented!();
 let mut queue: BinaryHeap<i32> = unimplemented!();

 let mut x = queue.peek_mut().unwrap();
 if p(*x) {
-    drop(x); // NLLだとこれでOK
-    queue.pop();
+    PeekMut::pop(x);
 } else {
     modify(&mut x);
 }

Minimum Rust Version: 1.19

a <= b <=> Reverse(a) >= Reverse(b)となるようなラッパー型です。

use std::cmp::Reverse;

let mut xs = vec![1, 2, 3, 4, 5];
xs.sort_unstable_by_key(|&x| Reverse(x));
assert_eq!(xs, &[5, 4, 3, 2, 1]);
use std::{cmp::Reverse, collections::BinaryHeap};

let mut max_queue = BinaryHeap::from(vec![1, 2]);
assert_eq!(max_queue.pop(), Some(2));
assert_eq!(max_queue.pop(), Some(1));
assert_eq!(max_queue.pop(), None);

let mut min_queue = BinaryHeap::from(vec![Reverse(1), Reverse(2)]);
assert_eq!(min_queue.pop(), Some(Reverse(1)));
assert_eq!(min_queue.pop(), Some(Reverse(2)));
assert_eq!(min_queue.pop(), None);

BinaryHeapのようなデータ構造は(f(x), x) (ただしfが単射またはxが一意)のような要素にすることで自由にキーを設定できますが、Reverseのようなラッパーなら簡潔かつ効率的になります。

-use std::collections::BinaryHeap;
+use std::{cmp::Reverse, collections::BinaryHeap};

 let (x, y, z): (i32, i32, i32) = unimplemented!();

 let mut queue = BinaryHeap::new();
-queue.push((u64::max_value() - x, x));
-queue.push((u64::max_value() - y, y));
-queue.push((u64::max_value() - z, z));
+queue.push(Reverse(x));
+queue.push(Reverse(y));
+queue.push(Reverse(z));

-let (_, min) = queue.pop().unwrap();
+let Reverse(min) = queue.pop().unwrap();
 assert_eq!(min, *[x, y, z].iter().min().unwrap());

Minimum Rust Version: 1.19

STDERRに吐くprint!, println!です。

-use std::io::{self, Write as _};
-
-macro_rules! eprintln(($($tt:tt)*) => (writeln!(io::stderr(), $($tt)*).unwrap()));

Minimum Rust Version: 1.20

「アルファベット1文字」が標準入力から与えられる問題は少なくありません。そのような問題でもこれでちょっとだけシンプルになります。

 static INPUT: &str = "a\n";

 let mut input = INPUT.split_ascii_whitespace();
 macro_rules! read(($ty:ty) => (input.next().unwrap().parse::<$ty>().unwrap()));

-let c = read!(String).as_bytes()[0];
+let c = read!(char) as u8;
 assert_eq!(c, b'a');

Minimum Rust Version: 1.20

unimplemented!("message")unimplemented!("{}", x)等を受け付けるようになります。

1.40で安定化されたtodo!も同様です。

if p1() {
    unimplemented!("あれをやる");
}
if p2() {
    todo!("これをやる");
}

std::option::Option::{get_or_insert, get_or_insert_with}

Minimum Rust Version: 1.20

あるOption<T>Someならその中身のTを、Noneなら代わりのTを詰めた上でそのT自体を、&mut Tとして得ます。

感覚としてはstd::collections::{btree_map, hash_map}::Entry::or_insert{, _with}と同じです。

let do_something = || unimplemented!();

let mut count: Option<i32> = unimplemented!();

if count.is_none() {
    do_something();
}
*count.get_or_insert(0) += 1;

Minimum Rust Version: 1.20

書いた瞬間その部分がコンパイルエラーになります。

マクロのエラー表示に使います。

 #[macro_export]
 macro_rules! foo {
     ($($expr:expr),+ $(,)?) => {
        $(
            foo($expr);
        )*
     };
+    ($($_tt:tt)*) => {
+        compile_error!("`foo!` takes 1 or more expressions")
+    };
 }

 fn foo(_: i32) {
     unimplemented!();
 }
+(@(state = Foo)) => {
+    compile_error!("TODO: あとで埋める")
+};

{float}::{from_bits, to_bits, {from, to}_{be, le, ne}_bytes}

Minimum Rust Version: 1.20 Minimum Rust Version: 1.32 Minimum Rust Version: 1.40

f64u64[u8; 8](ビット列表現)と相互変換します。

Minimum Rust Version: 1.20

unstable sortを行ないます。

 let mut values: Vec<i64> = unimplemented!();

-values.sort();
+values.sort_unstalbe();

str::into_boxed_bytes

Minimum Rust Version: 1.20

StringBox<str>にできます。

後述のBox::leakを使うとString&'staitc strにすることが可能です。

let input: String = "42\n".to_owned();
let input: Box<str> = input.into_boxed_str();
let input: &'static str = Box::leak(input);

Minimum Rust Version: 1.21

T: Copy, N > 32のときCopyではあり、dereferenceもできるがCloneではないという状態でしたが組み込みでCloneが実装されるようになりました。

Minimum Rust Version: 1.21

foreachします。

一応Rust 1.15.1では.map(..).last()で同様なことを行なえました。

-for x in XS
-    .iter()
+XS.iter()
     .filter(|&&x| p1(x))
     .map(|x| long_long_long_long_long_long_long_long_expr(x) + 1)
     .filter(|&x| p2(x))
-{
-    println!("{}", x);
-}
+    .for_each(|x| println!("{}", x));

std::mem::discriminant

Minimum Rust Version: 1.21

enum Tのオブジェクトに対し、Tのバリアントの種類について一意なオブジェクトを返します。

+use std::mem;
+
 enum Either<L, R> {
     Left(L),
     Right(R),
 }

 fn both_left<L, R>(a: Either<L, R>, b: Either<L, R>) -> bool {
-    matches!((a, b), (Either::Left(_), Either::Right(_)))
+    mem::discriminant(&a) == mem::discriminant(&b)
 }

Minimum Rust Version: 1.22

if let Some(..) = .. { .. }が何個もネストしているなら?に置き換えることでコードがシンプルになる場合があります。

 let f = || -> Option<i32> { unimplemented!() };
 let g = || -> Option<i32> { unimplemented!() };
 let h = || -> Option<i32> { unimplemented!() };
 let perform = |_: i32, _: i32, _: i32| unimplemented!();

-if let Some(x) = f() {
-    if let Some(y) = g() {
-        if let Some(z) = h() {
-            perform(x, y, z);
-        }
-    }
+if let Some((x, y, z)) = (|| {
+    let x = f()?;
+    let y = g()?;
+    let z = h()?;
+    Some((x, y, z))
+})() {
+    perform(x, y, z);
 }

Minimum Rust Version: 1.23 Minimum Rust Version: 1.26

 assert_eq!(
     long_long_long_long_long_long_expr(1),
-    long_long_long_long_long_long_expr(2)
+    long_long_long_long_long_long_expr(2),
 );

()FromIterator<()>, Extend<()>を実装

Minimum Rust Version: 1.23 Minimum Rust Version: 1.28

ジェネリックなコレクション型を扱うときにstd::io::{Empty, Sink}のような感覚で使えるかもしれません。

use std::{
    iter::{self, FromIterator},
    marker::PhantomData,
};

let mut wrapper = CollectionWrapper::<(), ()>::new(iter::once(()));
wrapper.push(());

struct CollectionWrapper<T, C> {
    collection: C,
    phantom: PhantomData<fn() -> T>,
}

impl<T, C: FromIterator<T> + Extend<T>> CollectionWrapper<T, C> {
    fn new(iter: impl IntoIterator<Item = T>) -> Self {
        Self {
            collection: iter.into_iter().collect(),
            phantom: PhantomData,
        }
    }

    fn push(&mut self, item: T) {
        self.collection.extend(iter::once(item));
    }
}

std::io::Cursorにいくつかのトレイトを実装

テスト関数をentry pointとしてデバッガでデバッグしたいとき等にこのような書き方をする場合があります。

use std::{
    io::{self, BufWriter, Read as _, Write},
    str::SplitAsciiWhitespace,
};

fn main() {
    let mut input = "".to_owned();
    io::stdin().read_to_string(&mut input).unwrap();

    let stdout = io::stdout();
    let mut stdout = BufWriter::new(stdout.lock());

    solve(input.split_ascii_whitespace(), &mut stdout);

    stdout.flush().unwrap();
}

fn solve(mut stdin: SplitAsciiWhitespace<'_>, mut stdout: impl Write) {
    macro_rules! read(($ty:ty) => (stdin.next().unwrap().parse::<$ty>().unwrap()));
    macro_rules! println(($($tt:tt)*) => (writeln!(stdout, $($tt)*).unwrap()));

    println!("Hello!");
}

#[cfg(test)]
mod tests {
    use std::{io::Cursor, str};

    #[test]
    fn test1() {
        test(INPUT, OUTPUT);

        static INPUT: &str = "";
        static OUTPUT: &str = "Hello!\n";
    }

    fn test(input: &str, output: &str) {
        let mut stdout = Cursor::new(vec![]);
        super::solve(input.split_ascii_whitespace(), &mut stdout);
        let stdout = str::from_utf8(stdout.get_ref()).unwrap();
        assert_eq!(stdout, output);
    }
}

このような用途でCursorを使う場合に少し違った書き方ができるようになります。

いくつかの型に対してFromによる変換が追加された

Minimum Rust Version: 1.17 Minimum Rust Version: 1.19 Minimum Rust Version: 1.21 Minimum Rust Version: 1.26 Minimum Rust Version: 1.28 Minimum Rust Version: 1.31 Minimum Rust Version: 1.36

Minimum Rust Version: 1.26

Box<T>のdropを放棄してリークし、&'a T where T: 'aにします。 つまりT: 'staticのとき&'static Tにできます。

このようなRustの標準入力ヘルパーを書きたいとしましょう。 str::split_(ascii_)whitespaceが使えそうですが、面倒なのがSplit(Ascii)Whitespace<'_>の取り扱いです。 特にインタラクティブ問題の対応等でEOFまで待たずに行ごとに読みたいというときに困ります。 実際、パフォーマンスを損なわずに上手くやるにはunsafeな手段を用いるか空白区切りの処理を自分で書くしかありませんでした。

Box::leakがあればString&'static strにすることで、行ごとに読む標準入力スキャナもunsafe無しで簡潔に書けるようになります。

use std::{
    fmt::Debug,
    io::BufRead,
    str::{FromStr, SplitAsciiWhitespace},
};

pub struct LineScanner<R: BufRead> {
    rdr: R,
    words: SplitAsciiWhitespace<'static>,
}

impl<R: BufRead> LineScanner<R> {
    pub fn new(rdr: R) -> Self {
        Self {
            rdr,
            words: "".split_ascii_whitespace(),
        }
    }

    pub fn next_value<T: FromStr>(&mut self) -> T
    where
        T::Err: Debug,
    {
        loop {
            if let Some(word) = self.words.next() {
                break word.parse().unwrap();
            }
            let mut line = "".to_owned();
            self.rdr.read_line(&mut line).unwrap();
            if line.is_empty() {
                panic!("reached EOF");
            }
            self.words = Box::leak(line.into_boxed_str()).split_ascii_whitespace();
        }
    }
}

Minimum Rust Version: 1.26

Option<&T>と同様にOption<&mut T>の中身をcloneしてOption<T>にします。

let mut xs = Some(vec![1, 2, 3]);
let xs: Option<&mut Vec<i32>> = xs.as_mut();
let _: Option<Vec<i32>> = xs.cloned();

Minimum Rust Version: 1.26

EntryOccupiedであるときに限り中身に変更を加え、Vacantのときは何もしないメソッドです。

 use std::{collections::HashMap, iter};

 let mut counter = iter::once((42, 1)).collect::<HashMap<_, _>>();
-if let Some(&v) = counter.get(&42) {
-    counter.insert(42, v + 1);
-}
+counter.entry(42).and_modify(|x| *x += 1);

 assert_eq!(counter[&42], 2);

Minimum Rust Version: 1.26

.next()Noneが出たら打ち止め」であることを示すIteratorのサブセットです。

Minimum Rust Version: 1.26

上記で紹介したstart..=end..=endに対応するinclusiveな範囲です。

rotate_left, rotate_right

Minimum Rust Version: 1.26 Minimum Rust Version: 1.36

左右にrotateします。

let mut xs = vec![1, 2, 3, 4, 5];
xs.rotate_left(1);
assert_eq!(xs, &[2, 3, 4, 5, 1]);

std::iter::DoubleEndedIterator::{rfind, rfold, try_rfold, nth_back}

Minimum Rust Version: 1.27 Minimum Rust Version: 1.37

後ろから処理するIterator::{find, fold, try_fold, nth}です。

use std::ops::Add;

let a: i32 = unimplemented!();
let b: i32 = unimplemented!();
let c: i32 = unimplemented!();

let array = [a, b, c];

assert_eq!(array.iter().find(|&&x| x == c), Some(&2));
assert_eq!(array.iter().rfind(|&&x| x == c), Some(&2));

assert_eq!(array.iter().fold(0, Add::add), a + b + c);
assert_eq!(array.iter().rfold(0, Add::add), a + b + c);

assert_eq!(array.iter().nth(2), Some(&c));
assert_eq!(array.iter().nth_back(2), Some(&a));

std::collections::{HashMap::{remove_entry, get_key_value}, BTreeMap::get_key_value}

Minimum Rust Version: 1.27 Minimum Rust Version: 1.40

Option<(&K, &V)>Option<(K, V)>が得られます。

Vec等の非Copyなキーを持っているときに役に立つかもしれません。

use std::collections::HashMap;

let mut map: HashMap<Vec<u8>, usize> = unimplemented!();

let _: Option<(&Vec<u8>, &usize)> = map.get_key_value(&b"foo"[..]);
let _: Option<(Vec<u8>, usize)> = map.remove_entry(&b"foo"[..]);

Minimum Rust Version: 1.27

中身がある条件を満たしていないSomeNoneにします。

use std::convert::Infallible;

assert_eq!(None::<Infallible>.filter(|_| false), None);
assert_eq!(None::<Infallible>.filter(|_| true), None);

assert_eq!(Some(2).filter(|n| n % 2 == 1), None);
assert_eq!(Some(3).filter(|n| n % 2 == 1), Some(3));
assert_eq!(Some(4).filter(|n| n % 2 == 1), None);

std::string::String::replace_range

Minimum Rust Version: 1.27

Stringに対し、ある範囲を別の文字列で置き換えます。

let mut s = "aaaaa#bbbbb#ccccc".to_owned();

if let [i, j] = *(0..s.len())
    .filter(|&i| s.as_bytes()[i] == b'#')
    .collect::<Vec<_>>()
{
    s.replace_range(i..=j, "@");
}

assert_eq!(s, "aaaaa@ccccc");

<[_]>::{rsplit, rsplit_mut}

Minimum Rust Version: 1.27

split, split_mutの逆順を得ます。

let s = b"foo|bar|baz"[..].to_owned();

assert_eq!(
    s.split(|&c| c == b'|').collect::<Vec<_>>(),
    &[b"foo", b"bar", b"baz"],
);

assert_eq!(
    s.rsplit(|&c| c == b'|').collect::<Vec<_>>(),
    &[b"baz", b"bar", b"foo"],
);

Minimum Rust Version: 1.27

要素数が等しい2つのスライスの中身を入れかえます。

異なる場合、パニックします。

let mut xs = [1, 2, 3, 0, 0, 0];
let mut ys = [0, 0, 0, 4, 5, 6];

xs[..3].swap_with_slice(&mut ys[..3]);

assert_eq!(xs, [0, 0, 0, 0, 0, 0]);
assert_eq!(ys, [1, 2, 3, 4, 5, 6]);
let mut xs = vec![1, 2, 3, 10, 20, 30];

assert_eq!(xs.len() % 2, 0);

let mid = xs.len() / 2;
let (left, right) = xs.split_at_mut(mid);
left.swap_with_slice(right);

assert_eq!(xs, &[10, 20, 30, 1, 2, 3]);

Minimum Rust Version: 1.28

n個おきの要素を生み出すイテレータを生成します。

Pythonの[start:end:step]のようなことができます。

TODO

todo!("なんか良い例");

Minimum Rust Version: 1.28

.or_insert_with(Default::defualt)の短縮です。

 use std::collections::HashMap;

 let mut map: HashMap<i32, Vec<u8>> = unimplemented!();

-for ch in map.entry(42).or_insert_with(Vec::new) {
+for ch in map.entry(42).or_default() {
     modify(ch);
 }

 fn modify(_: &mut u8) {
     unimplemented!();
 }

Minimum Rust Version: 1.28

Debug表記に凝りたければ...

Minimum Rust Version: 1.28

repeat_with版です。

+use std::iter;
+
 let gen_value = || -> i32 { unimplemented!() };
 let p = |_: i32| -> bool { unimplemented!() };

-let mut ans = vec![];
-loop {
-    let x = gen_value();
-    if p(x) {
-        ans.push(x);
-    } else {
-        break;
-    }
-}
-for ans in ans {
+for ans in iter::repeat_with(gen_value).take_while(|&x| p(x)) {
     println!("{}", ans);
 }

NonZero-整数型

Minimum Rust Version: 1.28 Minimum Rust Version: 1.34

0の整数型です。

Option<_>に対して最適化が行なわれます。

+use std::num::NonZeroUsize;
+
 let xs: &[i32] = unimplemented!();
-let size: usize = unimplemented!();
+let size: NonZeroUsize = unimplemented!();

-let outcome = if size > 0 {
-    *xs[..size].iter().max().expect("`size > 0`")
-} else {
-    unreachable!("多分0じゃないし大丈夫!");
-};
+let outcome = *xs[..size.get()]
+    .iter()
+    .max()
+    .expect("`size` is `NonZeroUsize`");

Minimum Rust Version: 1.28

usizeRange<usize>などスライスに対するindexを表わすトレイトです。

ただし1.44の現在でも各メソッドは安定化していないため、「構造体に含まれるVecに直にアクセスし、その結果を返すメソッド」のような使い道しか無いと思われます。

std::slice::{from_ref, from_mut}

Minimum Rust Version: 1.28

単一の&T&mut Tから&[T]&mut [T]を得ます。

それぞれunsafe { std::slice::from_raw_parts(x, 1) }, unsafe { std::slice::from_raw_parts_mut(x, 1) }の短縮です。

+use std::slice;
+
 struct Struct {
-    value: [i32; 1],
+    value: i32,
 }

 impl Values for Struct {
     type Item = i32;

     fn values(&self) -> &[i32] {
-         &self.value
+         slice::from_ref(&self.value)
     }
 }

 trait Values {
     type Item;
     fn values(&self) -> &[Self::Item];
 }

Minimum Rust Version: 1.29

.flat_map(|x| x)の短縮です。

-    .flat_map(std::convert::identity)
+    .flatten()

Minimum Rust Version: 1.29

Rc<dyn Trait>をダウンキャストします。

Minimum Rust Version: 1.30

FnMut(_) -> Option<_>を取り、最初のSomeの要素を返します。

.filter_map(f).next()の短縮です。

-    .filter_map(f)
-    .next();
+    .find_map(f);

cilppy::unnecessary_filter_map

Minimum Rust Version: 1.30

それぞれstr::{trim_left, trim_left_matches, trim_right, trim_right_matches}のリネームです。

実装は同じですが古いものはdeprecatedになります。

Minimum Rust Version: 1.31

「最後の余り」を捨てるchunks, chunks_mutです。

-for chunk in xs[..xs.len() - xs.len() % 3].chunks(3) {
+for chunk in xs.chunks_exact(3) {
     f(chunk);
 }

Minimum Rust Version: 1.31

右側から取るchunks_です。

-for chunk in xs[xs.len() % 3..xs.len()].chunks(3).rev() {
+for chunk in xs.rchunks() {
     f(chunk);
 }
-f(&xs[..xs.len() % 3]);

Minimum Rust Version: 1.31

std::mem::replace(option, Some(value))の短縮です。

let mut x = Some(1);
assert_eq!(x.replace(2), Some(1));
assert_eq!(x, Some(2));

let mut x = None;
assert_eq!(x.replace(1), None);
assert_eq!(x, Some(1));

Minimum Rust Version: 1.32

file!line!stringify!, {:#?}表記を並べたものをeprintln!します。

format_args!のように参照が取られるのではなく(T) -> Tの関数のように振る舞うので、非Copyな値はそのまま持っていかれてしまいます。 これは式の中のある部分をdbg!()で囲う用途のためです。 非Copyな値のムーブを防ぐには、deb(&expr)としましょう。

またやっている事が事なので非常に遅いです。 release buildでも消えないのでループ内に残した場合、まず間違いなくTLEの原因になります。

let xs = vec![1, 2, 3];
dbg!(&xs);
[src/main.rs:3] &xs = [
    1,
    2,
    3,
]

Minimum Rust Version: 1.33

恒等関数です。

|x| xのかわりに使うことができます。

+use std::convert;

-perform(|x| x);
+perform(convert::identity);

resize_with

Minimum Rust Version: 1.33

Vec::resize, VecDeque::resize_with版です。

-vec.resize(10, f());
+vec.resize_with(10, f);

Minimum Rust Version: 1.34

インスタンスを持たない型です。

用途としては!の代用で、そのためいくつかのトレイトを実装しています。 never_typeが安定化されれば!のエイリアスとなる予定ですが、今まで幾度となく安定化が試みられてはrevertされ続けているので当分先かもしれません。

use std::{convert::Infallible, marker::PhantomData};

struct Marker<T>(Infallible, PhantomData<fn() -> T>);
use std::{convert::Infallible, str::FromStr};

enum Enum {
    A,
    B,
    Other(String),
}

impl FromStr for Enum {
    type Err = Infallible;

    fn from_str(s: &str) -> Result<Self, Infallible> {
        match s {
            "a" => Ok(Self::A),
            "b" => Ok(Self::B),
            s => Ok(Self::Other(s.to_owned())),
        }
    }
}

std::convert::{TryFrom, TryInto}

Minimum Rust Version: 1.34

From, Intotry_版です。

ジェネリックな整数型を変換するのに使えます。

use std::{
    convert::{TryFrom, TryInto},
    fmt,
};

fn f<T: TryFrom<i128> + TryInto<i128>>(x: T, y: T) -> T
where
    <T as TryFrom<i128>>::Error: fmt::Debug,
    <T as TryInto<i128>>::Error: fmt::Debug,
{
    let x = x.try_into().unwrap();
    let y = y.try_into().unwrap();
    return f(x, y).try_into().unwrap();

    fn f(x: i128, y: i128) -> i128 {
        unimplemented!();
    }
}

Minimum Rust Version: 1.34

FnMut() -> Option<T>からイテレータを生成します。

todo!("なんか良い例");

Minimum Rust Version: 1.34

Option<T>FnMut(&T) -> Option<T>からイテレータを生成します。

todo!("なんか良い例");

Minimum Rust Version: 1.34

それぞれの要素に対して一度しか関数を呼ばないsort_by_keyです。

ToString::to_string等関数が重い場合に使えます。

{integer}::{checked_pow, saturating_pow, wrapping_pow, overflowing_pow}

Minimum Rust Version: 1.34

それぞれ{integer}::powchecked_, saturating_, wrapping_, overflowing_版です。

例えば以下の問題が簡単にできるようになります。

第三回 アルゴリズム実技検定 - C - 等比数列

let a: u64 = unimplemented!();
let r: u64 = unimplemented!();
let n: u32 = unimplemented!();

let x = a.saturating_mul(r.saturating_pow(n - 1));
if x > 10u64.pow(9) {
    println!("large");
} else {
    println!("{}", x);
}

copysign

Minimum Rust Version: 1.35

C++をはじめとした他の言語での同名な関数と同じく、片方の絶対値にもう片方の符号が付いた値を返します。

Minimum Rust Version: 1.35

FnOnce(&T) -> (&U, &T)を通し、あるRef<'a T>から(Ref<'a, T>, Ref<'a, U>)を得ます。

use std::cell::{Ref, RefCell};

let s = RefCell::new(b"user@pass"[..].to_owned());
let (user, pass) = Ref::map_split(s.borrow(), |s| {
    let words = s.splitn(2, |&c| c == b'@').collect::<Vec<_>>();
    (words[0], words[1])
});

Minimum Rust Version: 1.35

replace_with版です。

RangeFullを除いたRangeBoundsのインスタンスのcontains

Minimum Rust Version: 1.35

l <= x && x < r(l..r).contains(&x)と書けるようになります。

l..r等の範囲自体を取り回すときに便利です。

 use std::ops::Range;

 let x: i32 = unimplemented!();
 let range: Range<i32> = unimplemented!();

-let p = range.start <= x && x < range.end;
+let p = range.contains(&x);

copied

Minimum Rust Version: 1.35 Minimum Rust Version: 1.36

.cloned()と同じ働きをしますが対象がCopyに限定されていて、ディーブコピーしている感を出しません。

-    .cloned()
+    .copied()
     .collect::<Vec<_>>();
-    .map(|&x| x)
+    .copied()
     .collect::<Vec<_>>();

Minimum Rust Version: 1.37

&mut Tから&Cell<T>を得ます。

use std::cell::Cell;

let mut x: i32 = unimplemented!();

let x_ref1 = Cell::from_mut(&mut x);
let x_ref2 = x_ref1.clone();

Minimum Rust Version: 1.37

&[Cell<T>]から&Cell<[T]>を得ます。

use std::cell::Cell;

let x: i32 = unimplemented!();
let y: i32 = unimplemented!();
let z: i32 = unimplemented!();

let slice = &mut [x, y, z][..];
let slice = Cell::from_mut(slice).as_slice_of_cells();
let _: &[Cell<i32>] = slice;

Minimum Rust Version: 1.37

片方だけがSomeであるときかつそのときに限り、そのSomeを返します。

assert_eq!(None::<i32>.xor(None), None);
assert_eq!(Some(1).xor(None), Some(1));
assert_eq!(None.xor(Some(2)), Some(2));
assert_eq!(Some(1).xor(Some(2)), None);

reverse_bits

Minimum Rust Version: 1.37

ビットリバースを行ないます。

#![allow(clippy::unreadable_literal)]

let x = 0b00001011u8;
assert_eq!(x.reverse_bits(), 0b11010000);

Minimum Rust Version: 1.38

.peekable().take()したものから.rev()できるようになります。

let mut xs = xs.iter().peekable();
if **xs.peek().unwrap() < 10 {
    let a = xs.next().unwrap();
    let b = xs.rev().next().unwrap();
    println!("{}", a + b);
}

{,checked_, overflowing_, wrapping_}::euclid_{div, rem}

Minimum Rust Version: 1.38

余りが非負になるような形で行なう/%です。

assert_eq!(-7 / 3, -2);
assert_eq!((-7i32).div_euclid(3), -3);

std::option::Option::{as_deref, as_deref_mut}

Minimum Rust Version: 1.40

それぞれ.as_ref().map(Deref::deref), .as_mut().map(DerefMut::deref_mut)の短縮です。

&Option<Vec<T>>を一発でOption<&[T]>にすることができます。

NoneSome(vec![])が異なる意味を持ち、かつslice patternや文字列マッチで綺麗に処理できるといった場合に使えるでしょう。

assert_eq!(f({ None::<Vec<_>> }.as_deref()), 0);
assert_eq!(f({ Some(vec![]) }.as_deref()), 1);
assert_eq!(f({ Some(vec![0.0]) }.as_deref()), 2);

fn f(values: Option<&[f64]>) -> i32 {
    match values {
        None => 0,
        Some([]) => 1,
        Some([..]) => 2,
    }
}

Minimum Rust Version: 1.40

Option<Option<T>>Option<T>にします。

.and_then(std::convert::identity)の短縮です。

 let x: Option<Option<i32>> = unimplemented!();
-let _: Option<i32> = x.and_then(std::convert::identity);
+let _: Option<i32> = x.flatten();

Minimum Rust Version: 1.40

std::mem::replace(_, _Default::default())の短縮です。

use std::mem;

let mut count = 42;
assert_eq!(mem::take(&mut count), 42);
assert_eq!(count, 0);

Minimum Rust Version: 1.40

unimplemented!のようなものです。 unimplemented!と使いわけることが推奨されています。

let on_one = || unimplemented!();
let on_two = || unimplemented!();

match k {
    1 => on_one(),
    2 => on_two(),
    _ => todo!(),
}

Minimum Rust Version: 1.41

std::rc::Weak::{weak_count, strong_count}

Minimum Rust Version: 1.41

std::rc::Weakから弱参照と強参照のカウントを得られるようになります。

std::fmt::DebugMap::{key, value}

Minimum Rust Version: 1.42

.entry(key, value)のかわりに.key(key).value(value)と書けます。

用途としてはkeyvalueだけ別関数に切り出したいときでしょうか。 必要なとき以外はentryを使うことが推奨されています。

 use std::{collections::HashMap, fmt};

 struct ByteStringCounter(HashMap<Vec<u8>, usize>);

 impl fmt::Debug for ByteStringCounter {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
         let mut fmt = fmt.debug_map();
         for (key, value) in &self.0 {
-            fmt.entry(&String::from_utf8_lossy(key), value);
+            fmt.key(&String::from_utf8_lossy(key)).value(value);
         }
         fmt.finish()
     }
 }

Minimum Rust Version: 1.42

match $expression {
    $pattern if $guard => true,
    _ => false,
}

matches!($expression, $pattern if $guard)

と書けるようになります。

Optionにはmap_orというのがありましたが、それ以外の構造ではmatch ..か二重ifにする必要がありました。

新しいRustならslice pattern等で使う機会が出てくると思います。

-let p = xs.len() == 3 && xs[0] == 1 && (xs[1] - xs[2]).abs() < 2;
+let p = matches!(*xs, [1, x, y] if (x - y).abs() < 2);
-let p = b'a' <= c && c <= b'z' || b'0' <= c && c <= b'9' || c == b'-' || c == b'_';
+let p = matches!(c, b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_');
 enum Value {
     Int(i64),
     Float(f64),
     String(String),
 }

 impl Value {
     fn is_int(&self) -> bool {
-        match self {
-            Self::Int(_) => true,
-            _ => false,
-        }
+        matches!(self, Self::Int(_))
     }
 }

TODO: 要る?

The compiler's dead_code lint now accounts for type aliases.

Minimum Rust Version: 1.16

Uninhabitable enums (those without any variants) no longer permit wildcard match patterns

Minimum Rust Version: 1.16

std: Fix partial writes in LineWriter

Minimum Rust Version: 1.16

std: Clamp max read/write sizes on Unix

Minimum Rust Version: 1.16

Minimum Rust Version: 1.16

.ok().unwrap_or_default()の短縮です。

Minimum Rust Version: 1.17

Minimum Rust Version: 1.17

Iterator<Item = char>からは1.15.1でもできていました。

Minimum Rust Version: 1.17

Minimum Rust Version: 1.19

-let mut input = "foo bar baz\n".split_whitespace().peekable();
-let mut peek = || input.peek().copied();
+let mut input = "foo bar baz\n".split_whitespace();
+let peek = || input.clone().next();

 assert_eq!(peek(), Some("foo"));
 assert_eq!(input.next(), Some("foo"));
 assert_eq!(input.next(), Some("bar"));
 assert_eq!(input.next(), Some("baz"));
 assert_eq!(input.next(), None);

Minimum Rust Version: 1.19

Minimum Rust Version: 1.20

Minimum Rust Version: 1.20

Minimum Rust Version: 1.20

Minimum Rust Version: 1.20

Minimum Rust Version: 1.20

そもそもNaN出たらおわりでは?

str::from_utf8_mut

Minimum Rust Version: 1.20

str::{get, get_mut}

Minimum Rust Version: 1.20

Minimum Rust Version: 1.22

Minimum Rust Version: 1.23

std::panic::Location::column

Minimum Rust Version: 1.25

Minimum Rust Version: 1.26

std::fs::{read, read_to_string, write}

Minimum Rust Version: 1.26

関数とトレイトに#[must_use]を付けられるようになった (fn_must_use)

Minimum Rust Version: 1.27 Minimum Rust Version: 1.32

Minimum Rust Version: 1.27

std::iter::Iterator::{try_fold, try_for_each}

Minimum Rust Version: 1.27

Minimum Rust Version: 1.28

Minimum Rust Version: 1.29

Minimum Rust Version: 1.29

#[panic_handler] (panic_handler)

Minimum Rust Version: 1.30

#[panic_handler] does not work with std. - Issue #59222 - rust-lang/rust

Minimum Rust Version: 1.32

Box<[T]> now implements FromIterator<T>

Minimum Rust Version: 1.32

Minimum Rust Version: 1.33

Option<Result<T, E>> <-> Result<Option<T>, E>の変換を行ないます。

Minimum Rust Version: 1.34

Indexing a str is now generic over all types that implement SliceIndex<str>

Minimum Rust Version: 1.34

取り得る型は変わらないはず

Minimum Rust Version: 1.35

Removed the Read trait bounds on the BufReader::{get_ref, get_mut, into_inner} methods

Minimum Rust Version: 1.35

Minimum Rust Version: 1.36

BufReader::buffer, BufWriter::buffer

Minimum Rust Version: 1.37

Minimum Rust Version: 1.38

Minimum Rust Version: 1.38

Minimum Rust Version: 1.40

Result::{map_or, map_or_else}

Minimum Rust Version: 1.40

Minimum Rust Version: 1.41

Clone this wiki locally
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy