-
Notifications
You must be signed in to change notification settings - Fork 3
Since 1.15.1
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その他 |
- 1.36以降なら2015でもNLLが使える
- モジュールシステムの違いで2018を前提にしたライブラリが持ち込めない
可能性がある2018感覚で書くと割と引っ掛る
-
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より後に安定化された機能自体の更新(可能な限りまとめる)
- Stabilize Self and associated types in struct expressions and patterns #39282
- Stabilize self_struct_ctor feature. #56365
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(_) => {}
}
}
}
let param = 42;
-let _ = Struct { param: param };
+let _ = Struct { param };
struct Struct {
param: i32,
}
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>),
}
-static S: &'static str = "foo";
+static S: &str = "foo";
型境界にBorrow
が使われているHashMap
やBTreeMap
等にはあまり影響がありませんが、自分で実装した型に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
static FOO: Foo<'_> = Foo { text: TEXT };
static TEXT: &str = "text";
struct Foo<'a> {
text: &'a str,
}
pub
のかわりにpub(crate)
やpub(super)
、pub(in a::b::c)
と書けるようになります。
ただしcrate rootから到達できるpub
なアイテムはdead-code
のwarningが発生しないという利点があるので、この機能の出番はあまり無いかもしれません。
pub(crate) fn hello() {
println!("Hello!");
}
- 3.2.3. More visibility modifiers - The Edition Guide
- 6.16. Visibility and Privacy - The Rust Reference
TODO: どうbreakingか
macro_rules! foo {
($ty:ty) => {};
}
foo!(A + B + ?Sized);
マクロで使えるかもしれません。
let point = Point2(1.0, 2.0);
let point = Point2 { 0: 1.0, 1: 2.0 };
struct Point2(f64, f64);
こうしていたのが、
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;
}
};
- 3.4.1.
loop
s can break with a value - The Edition Guide break
and loop values - 8.2.14. Loop expressions - The Rust Reference
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
Associated Itemとしてconst
が追加できるようになりました。
trait ConstValue {
type Value: Copy;
- fn value() -> Self::Value;
+ const VALUE: Self::Value;
}
- 3.5.4. Associated constants - The Edition Guide
- Associated Constants - 6.15. Associated Items - The Rust Reference
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)
}
}
turbofish付きのpathを受理するようになりました。
macro_rules! pass_path {
($_path:path) => {};
}
pass_path!(Vec::<i32>::new);
2.7. Paths - The Rust Reference
例えばi32
はAdd<&'_ 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;
}
const FOO: Foo = Foo;
struct Foo;
impl Drop for Foo {
fn drop(&mut self) {}
}
以下の問題が修正されました。
Binary operator LHS type is fixed instead of being subtyped #45425
このようにuse
文を書けるようになります。
// rustfmtはこの量でも縦に引き伸ばす
use std::{
collections::BinaryHeap,
io::{self, Read as _},
};
ただ見た目はスッキリしますが書きやすいという訳ではないので、ライブラリをゆっくり書くときにこの書き方をすると良いでしょう。
3.2.4. Nested imports with use
- The Edition Guide
普通に書いた場合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,
}
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),
_ => {}
}
- 3.8.2.
..=
for inclusive ranges - The Edition Guide - 8.2.15. Range expressions - The Rust Reference
- Range patterns - 9. Patterns - The Rust Reference
TODO
- 関数の引数のimpl trait
- 関数の返り値の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)] } }
- 安定化間近!Rustのimpl Traitを今こそ理解する - 簡潔なQ
- 3.5.1.
impl Trait
for returning complex types with ease - The Edition Guide - 10.1.16. Impl trait type - The Rust Reference
TODO
let x: &Option<i32> = unimplemented!();
if let Some(x) = x {
let _: &i32 = x;
}
- その式、値ですか?場所ですか? - Speaker Deck
- 3.7.2. Default match bindings - The Edition Guide
- Place Expressions and Value Expressions - 8.2. Expressions - The Rust Reference
- 8.2.17.
match
expressions - The Rust Reference
#![allow(clippy::unreadable_literal)]
let x = 0u128;
let y = 0i128;
assert_eq!(u128::max_value(), 0xffffffffffffffffffffffffffffffff);
assert_eq!(i128::max_value(), 0x7fffffffffffffffffffffffffffffff);
- 3.8.3. 128 bit integers - The Edition Guide
- Integer types - 10.1.2. Numeric types - The Rust Reference
- Stabilize termination_trait, split out termination_trait_test - Pull Request #49162 - rust-lang/rust
- Stabilize unit tests with non-
()
return type - Pull Request #51298 - rust-lang/rust
main
とテスト関数の戻り値に()
以下の型が使えるようになります。
使い道があるとしたら、標準入出力用のユーティリティに対するテスト関数の戻り値をio::Result<()>
にすることくらいでしょうか。
[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;
}
}
}
}
- その式、値ですか?場所ですか? - Speaker Deck
- 3.6. Slice patterns - The Edition Guide
- Place Expressions and Value Expressions - 8.2. Expressions - The Rust Reference
- Slice patterns - 9. Patterns - The Rust Reference
- Removed 'proc' from the reserved keywords list #49699 - rust-lang/rust
- Implement RFC 2421, 'Keyword unreservations (pure, sizeof, alignof, offsetof) - Pull Request #51196 - rust-lang/rust
1.0以前に使われていて、今は使われていない単語が解放されました。
2.2. Keywords - The Rust Reference
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 {}
bare-trait-object
- 3.5.2.
dyn Trait
for trait objects - The Edition Guide - 10.1.15. Trait object types - The Edition Guide
- Stabilize attributes on generic parameters - Pull Request #48851 - rust-lang/rust
- Support
cfg
andcfg_attr
on generic parameters - Pull Request #61547 - rust-lang/rust - Stabilize
param_attrs
in Rust 1.39.0 - Pull Request #64010 - rust-lang/rust
TODO
macro_rules!
にメタ変数のタイプとしてlifetime
, vis
, literal
が追加された (macro_lifetime_matcher
, macro_vis_matcher
, macro_literal_matcher
)
- stabilize macro_lifetime_matcher - Pull Request #50385 - rust-lang/rust
- Stabilize macro_vis_matcher - Pull Request #53370 - rust-lang/rust
- stabilize macro_literal_matcher - Pull Request #56072 - rust-lang/rust
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
先頭に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
- [beta] Stabilize crate_in_paths, extern_absolute_paths and extern_prelude on all editions. by eddyb · Pull Request #54404 · rust-lang/rust
- resolve: Scale back hard-coded extern prelude additions on 2015 edition by petrochenkov · Pull Request #54671 · rust-lang/rust
ローカルのアイテムを絶対パスで書くときに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::
が必須になります。
TODO
外部クレートのマクロをuse
できるようになったついでに、クレート内でのマクロの前方参照ができるようになりました。
このようにマクロをソースコードの下側で宣言できるようになります。
fn main() {
foo!();
}
mod _macros {
#[macro_export]
macro_rules! foo(() => (println!("foo")));
}
3.10.2. Macro changes - The Edition Guide
TODO
TODO
TODO: cargo fix --edition
-
[breaking-change] キーワードの追加
dyn
がweak keywordからstrong keywordになり、async
,await
,try
が追加されました。各競技プログラミングプラットフォームが2018 editionかの判定に使うことができます。 具体的には次のコードを提出すれば判定できます。
fn main() { let dyn = 42; }
-
[breaking-change] トレイトのassociated functionで
fn f(i32)
のような書き方ができなくなったRust 2015ではトレイトのassociated functionに限りこのような書き方ができましたが、Rust 2018では字句解析の時点で失敗するようになります。
trait Foo { fn method(&str) {} }
-
[breaking-change]
tyvar_behind_raw_pointer
がデフォルトでdeny
になったTODO
-
[breaking-change] ローカルのアイテムを絶対パスで
use
するときにcrate::
が必須になったローカルのアイテムの絶対パスを書くのに上記で説明した
crate::
が必須になります。 -
外部クレートを使うのに
extern crate
が不要になった- [beta] Stabilize crate_in_paths, extern_absolute_paths and extern_prelude on all editions. by eddyb · Pull Request #54404 · rust-lang/rust
- resolve: Scale back hard-coded extern prelude additions on 2015 edition by petrochenkov · Pull Request #54671 · rust-lang/rust
AtCoderの2020年環境やCodinGameで利用可能なクレートを使うにあたって、
extern crate
は不要です。-extern crate itertools; -extern crate rand; -extern crate regex;
-
No more
mod.rs
TODO
for 2018 edition
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 の借用検査は組込の複合代入演算子に対して特殊な動作を行う - 何とは言わない天然水飲みたさ
- NLL のおかげで Rust で平衡二分木を実装できた - @nojima's blog
- 3.7.1. Non-lexical lifetimes - The Edition Guide
- Non-lexical lifetimes - Announcing Rust 1.31 and Rust 2018 - Rust Blog
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`.
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
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();
}
- 3.7.3.
'_
, the anonymouse lifetime - The Edition Guide - 3.7.4. Lifetime elision in impl - The Edition Guide
elided-lifetime-in-path
- Stabilize
min_const_fn
- Pull Request #54835 - rust-lang/rust - Stabilize
let
bindings and destructuring in constants and const fn - Pull Request #57175 - rust-lang/rust - Stabilize min_const_unsafe_fn in 1.33 - Pull Request #57067 - rust-lang/rust
TODO: const fn
化したstd
の関数
TODO
-
clippy
がrustc
にとって既知のツールになった
match $expr {
$pattern => {}
}
を
match $expr {
($pattern) => {}
}
と書けるようになりました。(unused_parens
とかには引っ掛ります)
for 2018 edition
for 2015 edition
*
が0以上、+
が1以上の繰り返しを指定するのに対し、?
は「0回か1回」を指定します。
echo!(42);
echo!(42,); // trailing comma
#[macro_export]
macro_rules! echo {
($expr:expr $(,)?) => {
::std::println!("{}", $expr)
};
}
TODO
use Foo::A;
const FOO: Foo = A;
enum Foo {
A,
B,
}
use std::marker::PhantomData;
const PHANTOM: PhantomData<()> = PhantomData;
match PhantomData {
PHANTOM => {}
}
let x: u8 = unimplemented!();
match x {
0..=255 => unimplemented!(),
- _ => unreachable!("should be exhaustive"),
}
if let Foo::A(_) | Foo::B(_) = unimplemented!() {
todo!();
}
enum Foo {
A(i32),
B(i32),
C(i32),
}
if let
とwhile let
でirrefutable patternがコンパイルエラーからwarning(irrefutable_let_patterns
)になった (irrefutable_let_patterns
)
let .. = ..;
に置き換えられるif let
とloop { 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,
}
トレイトのメソッドだけ使いたいときに、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 _};
Rc
, Arc
, Pin
がレシーバーとして使えるようになったほか、Box<Box<Self>>
のようにネストできるようになりました。
- Stabilize
Rc
,Arc
andPin
as method receivers #56805 - Stabilize nested self receivers in 1.41.0 #64325
use std::rc::Rc;
use std::sync::Arc;
struct Foo;
impl Foo {
fn method(self: &mut Rc<Arc<Box<&Self>>>) {
todo!();
}
}
TODO: ライブラリを書くときに使える..?
参照で呼び出せる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
これにより、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!("こんにちは"),
}
}
}
static_assert_size!
等のマクロが書きやすくなります。
const _: () = {
let _: fn(usize, usize) -> usize = usize::saturating_sub;
};
recursion_limit
のデフォルト値は1.42.0時点で128
になっています。
リリースノートやRust Blogには記されていませんが、128
になったのは1.37.0の時点のようです。
macro_rules! rec {
() => {};
(_ $($rest:tt)*) => {
rec!($($rest)*);
}
}
rec!(
// 127個
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _
);
1.38以前ではby move patternではif
ガードを使えませんでした。
todo!("なんか良い例");
1.39以前ではコンストラクタ自体はconst
内で使えましたが、値として扱うとconst fn
ではなくなっていました。
const _: () = {
struct Foo(i32);
let _ = { Foo }(42);
};
impl<A, B, C> ForeignTrait<LocalType> for ForeignType<A, B, C> {
// ...
}
の形式でのimpl
が可能になります。 型引数無しなら以前から可能でした。
例にあるようなFrom
やAsRef
のほか、[T]
やVec<T>
にIndex<MyInt>
を実装させるといったことができます。
- Rustの競技プログラミング用万能int型 - Qiita
- RFC 2451
- Stabilize the
re_rebalance_coherence
feature #65879 - Relaxed restrictions when implementing traits - Announcing Rust 1.41.0 - Rust Blog
TODO: 1.44の今はどうなんだろう?
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()));
Vec::truncate
のようなことができます。
queue.truncate(1);
Vec::resize
のようなことができます。
queue.resize(n, (0, 0));
ある位置に&str
を挿入できます。
acc.insert_str(i, s);
回数制限のあるreplace
です。
assert_eq!(
"aaa aaa aaa aaa".replacen("aaa", "bbb", 2),
"bbb bbb aaa aaa",
);
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}
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]);
Vec::split_off
のString
版です。
let mut left = "abcdef".to_owned();
let right = left.split_off(left.len() / 2);
assert_eq!(left, "abc");
assert_eq!(right, "def");
「最後にキューがある状態かどうかが答え」という場合に使えるかもしれません。
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]);
左側が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}
RangeBounds
はa..b
やa..
, ..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!();
}
std::collections::BTreeMap::range
std::collections::BTreeMap::range_mut
std::collections::BTreeSet::range
std::collections::BTreeSet::range_mut
指定したRangeBounds
に収まる値のイテレータを返します。
C++のstd::lower_bound
やstd::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>::count
もIterator
のデフォルト実装が使われるっぽいのでC++のstd::partition_map
のように使おうとすると多分大惨事になる
ラッパー型に付き物のinto_inner
です。
use std::cell::Cell;
let x: i32 = unimplemented!();
let _: i32 = Cell::new(x).into_inner();
Cell
とRefCell
に対して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);
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));
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);
}
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());
STDERRに吐くprint!
, println!
です。
-use std::io::{self, Write as _};
-
-macro_rules! eprintln(($($tt:tt)*) => (writeln!(io::stderr(), $($tt)*).unwrap()));
「アルファベット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');
unimplemented!("message")
やunimplemented!("{}", x)
等を受け付けるようになります。
1.40で安定化されたtodo!
も同様です。
if p1() {
unimplemented!("あれをやる");
}
if p2() {
todo!("これをやる");
}
std::option::Option::{get_or_insert, get_or_insert_with}
ある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;
書いた瞬間その部分がコンパイルエラーになります。
マクロのエラー表示に使います。
#[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: あとで埋める")
+};
f64
をu64
や[u8; 8]
(ビット列表現)と相互変換します。
<[_]>::{sort_unstable, sort_unstable_by, sort_unstable_by_key}
unstable sortを行ないます。
let mut values: Vec<i64> = unimplemented!();
-values.sort();
+values.sort_unstalbe();
String
をBox<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);
T: Copy
, N > 32
のときCopy
ではあり、dereferenceもできるがClone
ではないという状態でしたが組み込みでClone
が実装されるようになりました。
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));
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)
}
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);
}
- The
?
operator for easier error handling - The Edition Guide - A Shortcut for Propagating Errors: the
?
Operator - Recoverable Errors with Result - The Rust Programming Language
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),
);
ジェネリックなコレクション型を扱うときに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
を使う場合に少し違った書き方ができるようになります。
-
for <T: Copy>
でBox<[T]>
にFrom<&'_ [T]>
を、Box<str>
にFrom<&'_ str>
を実装let _ = Box::<[u8]>::from(&b"foo"[..]); let _ = Box::<str>::from("foo");
-
for <T>
でVec<T>
にFrom<&mut T>
を実装let _ = Vec::from(&[42][..]); // `<Vec<i32> as From<&'_ [i32]>>::from` let _ = Vec::from(&mut [42][..]); // `<Vec<i32> as From<&'_ mut [i32]>>::from`
-
use std::rc::Rc; let _ = Rc::<[i32]>::from(&[42][..]); let _ = Rc::<[i32]>::from(vec![42]); let _ = Rc::<str>::from("foo"); let _ = Rc::<str>::from("foo".to_owned()); let _ = Rc::<str>::from("foo".to_owned().into_boxed_str());
-
usize
にFrom<u16>
を、isize
にFrom<{u8, i16}>
を実装let _: usize = 0u16.into(); let _: isize = 0u8.into(); let _: isize = 0i16.into();
-
if p { 1 } else { 0 }
をi32::from(p)
と書けるようになります。 逆はできません。(こちらはx != 0
で良いですし)-f() + if p() { 1 } else { 0 } +f() + i64::from(p())
-if p() { - ans += 1; -} +ans += u32::from(p());
-
Option::{as_ref, as_mut}
相当の変換をFrom
で行なえるようになったOption<&'a T>
がFrom<&'a Option<T>>
を、Option<&'a mut T>
がFrom<&'a mut Option<T>>
を実装するようになりました。let mut x: Option<i32> = unimplemented!(); modify(&mut x); fn modify<'a>(x: impl Into<Option<&'a mut i32>>) { if let Some(x) = x.into() { *x += 1; } }
-
String
がFrom<&String>
を実装let s = "hi".to_owned(); -f(&*s); +f(&s); fn f(_: impl Into<String>) { todo!(); }
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();
}
}
}
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();
std::collections::{btree_map::Entry::and_modify, hash_map::Entry::and_modify}
Entry
がOccupied
であるときに限り中身に変更を加え、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);
「.next()
でNone
が出たら打ち止め」であることを示すIterator
のサブセットです。
std::ops::{RangeInclusive, RangeToInclusive}
上記で紹介したstart..=end
と..=end
に対応するinclusiveな範囲です。
<[_]>::rotate_left
<[_]>::rotate_right
std::collections::VecDeque::rotate_left
std::collections::VecDeque::rotate_right
左右にrotateします。
let mut xs = vec![1, 2, 3, 4, 5];
xs.rotate_left(1);
assert_eq!(xs, &[2, 3, 4, 5, 1]);
後ろから処理する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}
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"[..]);
中身がある条件を満たしていないSome
をNone
にします。
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);
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}
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"],
);
要素数が等しい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]);
n個おきの要素を生み出すイテレータを生成します。
Pythonの[start:end:step]
のようなことができます。
TODO
todo!("なんか良い例");
std::collections::{btree_map::Entry::or_default, hash_map::Entry::or_default}
.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!();
}
std::fmt::{Formatter::align, Alignment}
Debug
表記に凝りたければ...
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);
}
std::num::NonZeroU8
std::num::NonZeroU16
std::num::NonZeroU32
std::num::NonZeroU64
std::num::NonZeroU128
std::num::NonZeroUsize
std::num::NonZeroI8
std::num::NonZeroI16
std::num::NonZeroI32
std::num::NonZeroI64
std::num::NonZeroI128
std::num::NonZeroIsize
非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`");
usize
やRange<usize>
などスライスに対するindexを表わすトレイトです。
ただし1.44の現在でも各メソッドは安定化していないため、「構造体に含まれるVec
に直にアクセスし、その結果を返すメソッド」のような使い道しか無いと思われます。
単一の&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];
}
.flat_map(|x| x)
の短縮です。
- .flat_map(std::convert::identity)
+ .flatten()
Rc<dyn Trait>
をダウンキャストします。
FnMut(_) -> Option<_>
を取り、最初のSome
の要素を返します。
.filter_map(f).next()
の短縮です。
- .filter_map(f)
- .next();
+ .find_map(f);
cilppy::unnecessary_filter_map
str::{trim_start, trim_start_matches, trim_end, trim_end_matches}
それぞれstr::{trim_left, trim_left_matches, trim_right, trim_right_matches}
のリネームです。
実装は同じですが古いものはdeprecatedになります。
<[_]>::{chunks_exact, chunks_exact_mut}
「最後の余り」を捨てるchunks
, chunks_mut
です。
-for chunk in xs[..xs.len() - xs.len() % 3].chunks(3) {
+for chunk in xs.chunks_exact(3) {
f(chunk);
}
<[_]>::{rchunks, rchunks_mut, rchunks_exact, rchunks_exact_mut}
右側から取る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]);
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));
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,
]
恒等関数です。
|x| x
のかわりに使うことができます。
+use std::convert;
-perform(|x| x);
+perform(convert::identity);
Vec::resize
, VecDeque::resize
の_with
版です。
-vec.resize(10, f());
+vec.resize_with(10, f);
インスタンスを持たない型です。
用途としては!
の代用で、そのためいくつかのトレイトを実装しています。
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())),
}
}
}
ジェネリックな整数型を変換するのに使えます。
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!();
}
}
FnMut() -> Option<T>
からイテレータを生成します。
todo!("なんか良い例");
Option<T>
とFnMut(&T) -> Option<T>
からイテレータを生成します。
todo!("なんか良い例");
それぞれの要素に対して一度しか関数を呼ばないsort_by_key
です。
ToString::to_string
等関数が重い場合に使えます。
それぞれ{integer}::pow
のchecked_
, saturating_
, wrapping_
, overflowing_
版です。
例えば以下の問題が簡単にできるようになります。
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);
}
C++をはじめとした他の言語での同名な関数と同じく、片方の絶対値にもう片方の符号が付いた値を返します。
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])
});
replace
の_with
版です。
RangeFull
を除いたRangeBounds
のインスタンスのcontains
std::ops::Range::contains
std::ops::RangeFrom::contains
std::ops::RangeTo::contains
std::ops::RangeInclusive::contains
std::ops::RangeToInclusive::contains
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);
.cloned()
と同じ働きをしますが対象がCopy
に限定されていて、ディーブコピーしている感を出しません。
- .cloned()
+ .copied()
.collect::<Vec<_>>();
- .map(|&x| x)
+ .copied()
.collect::<Vec<_>>();
&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();
&[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;
片方だけが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);
ビットリバースを行ないます。
#![allow(clippy::unreadable_literal)]
let x = 0b00001011u8;
assert_eq!(x.reverse_bits(), 0b11010000);
.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);
}
{integer}::div_euclid
{integer}::checked_div_euclid
{integer}::overflowing_div_euclid
{integer}::wrapping_div_euclid
{integer}::rem_euclid
{integer}::checked_rem_euclid
{integer}::overflowing_rem_euclid
{integer}::wrapping_rem_euclid
余りが非負になるような形で行なう/
と%
です。
assert_eq!(-7 / 3, -2);
assert_eq!((-7i32).div_euclid(3), -3);
std::option::Option::{as_deref, as_deref_mut}
それぞれ.as_ref().map(Deref::deref)
, .as_mut().map(DerefMut::deref_mut)
の短縮です。
&Option<Vec<T>>
を一発でOption<&[T]>
にすることができます。
None
とSome(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,
}
}
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();
std::mem::replace(_, _Default::default())
の短縮です。
use std::mem;
let mut count = 42;
assert_eq!(mem::take(&mut count), 42);
assert_eq!(count, 0);
unimplemented!
のようなものです。
unimplemented!
と使いわけることが推奨されています。
let on_one = || unimplemented!();
let on_two = || unimplemented!();
match k {
1 => on_one(),
2 => on_two(),
_ => todo!(),
}
std::rc::Weak::{weak_count, strong_count}
std::rc::Weak
から弱参照と強参照のカウントを得られるようになります。
.entry(key, value)
のかわりに.key(key).value(value)
と書けます。
用途としてはkey
やvalue
だけ別関数に切り出したいときでしょうか。
必要なとき以外は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()
}
}
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(_))
}
}
.ok().unwrap_or_default()
の短縮です。
Iterator<Item = char>
からは1.15.1でもできていました。
-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);
そもそもNaN出たらおわりでは?
- stabilize
#[must_use]
for functions and must-use comparison operators (RFC 1940) - Pull Request #48925 - rust-lang/rust - Allow #[must_use] on traits - Pull Request #55663 - rust-lang/rust
std::iter::Iterator::{try_fold, try_for_each}
#[panic_handler]
does not work with std. - Issue #59222 - rust-lang/rust
Option<Result<T, E>>
<-> Result<Option<T>, E>
の変換を行ないます。
取り得る型は変わらないはず