-
Notifications
You must be signed in to change notification settings - Fork 3
Since 1.15.1
Rustについて何かを調べたいときはまずはこちらから:
2020-06-03時点のものです。
fn main() { let dyn = 42; }やfn main() { let _ = matches!(1, 1); }等のコードを提出することでversionとeditionを割り出しています。
| プラットフォーム | version |
edition |
opt-level |
外部クレート |
|---|---|---|---|---|
| AtCoder 2020年環境 | 1.42.0 |
2018 |
3 |
39個 |
| Codeforces | 1.42.0 |
2018 |
? | 0個 |
| CodinGame | 1.38.0 |
2018 |
? | 6個 |
| Library-Checker | 1.41.0 |
2015 |
2 |
0個 |
| yukicoder | 1.42.0 |
2015 |
2 |
0個 |
| TODO: AOJとか |
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.
loops can break with a value - The Edition Guide breakand 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 Traitfor 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.
matchexpressions - 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 Traitfor 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
cfgandcfg_attron generic parameters - Pull Request #61547 - rust-lang/rust - Stabilize
param_attrsin 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.rsTODO
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 errorTODO
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
letbindings 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,ArcandPinas 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_coherencefeature #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::rangestd::collections::BTreeMap::range_mutstd::collections::BTreeSet::rangestd::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_rightstd::collections::VecDeque::rotate_leftstd::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::NonZeroU8std::num::NonZeroU16std::num::NonZeroU32std::num::NonZeroU64std::num::NonZeroU128std::num::NonZeroUsizestd::num::NonZeroI8std::num::NonZeroI16std::num::NonZeroI32std::num::NonZeroI64std::num::NonZeroI128std::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::containsstd::ops::RangeFrom::containsstd::ops::RangeTo::containsstd::ops::RangeInclusive::containsstd::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>の変換を行ないます。
取り得る型は変わらないはず