あずんひ(@aznhe21)です。みなさんGWはどこ行きましたか?私はこれから考えます。
さて、いつもは会社のテックブログで投稿してる深掘りシリーズですが、 Rust 1.52がGW中にリリースされるということで(大晦日リリースのRust 1.49に引き続き)個人ブログからお送りします。
ピックアップ
個人的に注目する変更点を「ピックアップ」としてまとめました。 全ての変更点を網羅したリストは変更点リストをご覧ください。
unsafe関数内でのみだりなunsafe処理を防げるようになった
これまではunsafe関数内では全てのunsafeな処理が許可されていました。 しかし、これでは関数自体はunsafeであっても処理の大半が安全である場合に、安全な処理との区別がしづらいという問題があります。
unsafe fn read_f32(x: &[u8]) -> Option<f32> { if x.len() < std::mem::size_of::<f32>() { return None; } // 危険なのはこの行だけ let r = std::ptr::read_unaligned(x as *const _ as *const f32); Some(r) }
そこでunsafe関数の中でunsafeブロックを使って危険な処理をマークすることを思い付きますが、 もともとunsafe関数内では全てunsafeな処理として見做されているためコンパイラに警告されてしまいます。
unsafe fn read_f32(x: &[u8]) -> Option<f32> { if x.len() < std::mem::size_of::<f32>() { return None; } let r = unsafe { std::ptr::read_unaligned(x as *const _ as *const f32) }; // ^^^^^^ unnecessary `unsafe` block Some(r) }
Rust 1.52ではunsafe_op_in_unsafe_fn
というリントが追加され、unsafeな関数直下ではunsafeな処理を警告ないし禁止することが出来るようになりました。
これによりunsafe関数内でのunsafeブロックが意味を持つようになるのです。
#[deny(unsafe_op_in_unsafe_fn)] // 関数内での直接のunsafe処理を禁止する unsafe fn read_f32(x: &[u8]) -> Option<f32> { if x.len() < std::mem::size_of::<f32>() { return None; } // SAFETY: xはf32の大きさ以上であり、読み取っても問題は無い let r = unsafe { std::ptr::read_unaligned(x as *const _ as *const f32) }; Some(r) }
安定化されたAPIのドキュメント
安定化されたAPIのドキュメントを独自に訳して紹介します。リストだけ見たい方は安定化されたAPIをご覧ください。
Arguments::as_str
impl<'a> Arguments<'a> { #[stable(feature = "fmt_as_str", since = "1.52.0")] #[inline] pub fn as_str(&self) -> Option<&'static str> { /* 実装は省略 */ } }
フォーマットすべき引数が無い場合、フォーマット済み文字列を取得する。
最も些細なケースで、メモリ確保を避けるために使うことが出来る。
サンプル
use std::fmt::Arguments; fn write_str(_: &str) { /* ... */ } fn write_fmt(args: &Arguments) { if let Some(s) = args.as_str() { write_str(s) } else { write_str(&args.to_string()); } }
assert_eq!(format_args!("hello").as_str(), Some("hello")); assert_eq!(format_args!("").as_str(), Some("")); assert_eq!(format_args!("{}", 1).as_str(), None);
char::MAX
#[lang = "char"] impl char { #[stable(feature = "assoc_char_consts", since = "1.52.0")] pub const MAX: char = '\u{10ffff}'; }
char
が持つことの出来る、最大の有効なコードポイント。
char
はUnicodeのスカラ値つまりコードポイントであるが、ある範囲に限られる。
MAX
は有効なUnicodeのスカラ値の中で最大の有効なコードポイントである。
char::REPLACEMENT_CHARACTER
#[lang = "char"] impl char { #[stable(feature = "assoc_char_consts", since = "1.52.0")] pub const REPLACEMENT_CHARACTER: char = '\u{FFFD}'; }
U+FFFD REPLACEMENT CHARACTER
(�)はUnicodeでデコードエラーを表すのに使われる。
例えば不正なUTF-8バイト列をString::from_utf8_lossy
に渡した場合に発生する。
char::UNICODE_VERSION
#[lang = "char"] impl char { #[stable(feature = "assoc_char_consts", since = "1.52.0")] pub const UNICODE_VERSION: (u8, u8, u8) = crate::unicode::UNICODE_VERSION; }
char
やstr
のメソッドでのUnicode部分が基づくUnicodeのバージョン。
Unicodeの新バージョンは定期的にリリースされるが、それに伴って標準ライブラリのUnicodeに依存する全てのメソッドも更新される。
従って、時間と共にchar
とstr
のメソッドやこの定数の値は変更される。これは破壊的変更とは見做されない。
バージョン番号の体型は Unicode 11.0 or later, Section 3.1 Versions of the Unicode Standard で説明されている。
char::decode_utf16
#[lang = "char"] impl char { #[stable(feature = "assoc_char_funcs", since = "1.52.0")] #[inline] pub fn decode_utf16<I: IntoIterator<Item = u16>>(iter: I) -> DecodeUtf16<I::IntoIter> { /* 実装は省略 */ } }
iter
内のUTF-16でエンコードされたコードポイントのイテレータを生成する。
ペアになっていないサロゲートはErr
として返す。
サンプル
基本的な用法:
use std::char::decode_utf16; // 𝄞mus<不正>ic<不正> let v = [ 0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0xDD1E, 0x0069, 0x0063, 0xD834, ]; assert_eq!( decode_utf16(v.iter().cloned()) .map(|r| r.map_err(|e| e.unpaired_surrogate())) .collect::<Vec<_>>(), vec![ Ok('𝄞'), Ok('m'), Ok('u'), Ok('s'), Err(0xDD1E), Ok('i'), Ok('c'), Err(0xD834) ] );
Err
を置換文字に置き換えることで不可逆なデコーダーを得られる。
use std::char::{decode_utf16, REPLACEMENT_CHARACTER}; // 𝄞mus<不正>ic<不正> let v = [ 0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0xDD1E, 0x0069, 0x0063, 0xD834, ]; assert_eq!( decode_utf16(v.iter().cloned()) .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER)) .collect::<String>(), "𝄞mus�ic�" );
char::from_digit
#[lang = "char"] impl char { #[stable(feature = "assoc_char_funcs", since = "1.52.0")] #[inline] pub fn from_digit(num: u32, radix: u32) -> Option<char> { /* 実装は省略 */ } }
与えられた基数で数値をchar
に変換する。
'radix'はしばしば'base'とも呼ばれる。いくつかの一般的な値において、基数2は2進数を、基数10は10進数を、基数16は16進数を表す。 また、任意の基数がサポートされる。
入力が指定された基数の数字でない場合、from_digit()
はNone
を返す。
パニック
基数が36より大きい場合はパニックする。
サンプル
基本的な用法:
use std::char; let c = char::from_digit(4, 10); assert_eq!(Some('4'), c); // 10進数の11は、16進数では1桁となる let c = char::from_digit(11, 16); assert_eq!(Some('b'), c);
入力が数字でない場合はNone
を返す。
use std::char; let c = char::from_digit(20, 10); assert_eq!(None, c);
大きい基数を渡した場合パニックを引き起こす。
use std::char; // パニックする char::from_digit(1, 37);
char::from_u32_unchecked
#[lang = "char"] impl char { #[stable(feature = "assoc_char_funcs", since = "1.52.0")] #[inline] pub unsafe fn from_u32_unchecked(i: u32) -> char { /* 実装は省略 */ } }
正当性を無視してu32
をchar
に変換する。
全てのchar
はu32
として有効であり、キャストが出来ることに注意されたい。
let c = '💯'; let i = c as u32; assert_eq!(128175, i);
しかし逆は言えず、u32
の値が全てchar
として有効というわけではない。
from_u32_unchecked()
はこれを無視し、不正な値を生成するかもしれないまま盲目的にchar
にキャストする。
安全性
不正はchar
の値を生成するかもしれないため、この関数はunsafeである。
この関数の安全なバージョンはfrom_u32
関数を参照されたい。
サンプル
基本的な用法:
use std::char; let c = unsafe { char::from_u32_unchecked(0x2764) }; assert_eq!('❤', c);
char::from_u32
#[lang = "char"] impl char { #[stable(feature = "assoc_char_funcs", since = "1.52.0")] #[inline] pub fn from_u32(i: u32) -> Option<char> { /* 実装は省略 */ } }
char
をu32
に変換する。
全てのchar
はu32
として有効であり、キャストが出来ることに注意されたい。
let c = '💯'; let i = c as u32; assert_eq!(128175, i);
しかし逆は言えず、u32
の値が全てchar
として有効というわけではない。
入力がchar
として有効な値でない場合、from_u32()
はNone
を返す。
これらのチェックをしない、unsafeなバージョンの関数はfrom_u32_unchecked
を参照されたい。
サンプル
基本的な用法:
use std::char; let c = char::from_u32(0x2764); assert_eq!(Some('❤'), c);
入力が不適切なchar
の場合、None
を返す。
use std::char; let c = char::from_u32(0x110000); assert_eq!(None, c);
slice::partition_point
#[lang = "slice"] #[cfg(not(test))] impl<T> [T] { #[stable(feature = "partition_point", since = "1.52.0")] pub fn partition_point<P>(&self, mut pred: P) -> usize where P: FnMut(&T) -> bool, { /* 実装は省略 */ } }
与えられた述語関数において分割位置となるインデックスを返す(2つ目の区画での最初の要素のインデックス)。
スライスは与えられた述語関数で分割されていると仮定される。
つまり、述語関数がtrue
を返す全ての要素はスライスの先頭に、述語関数がfalse
を返す全ての要素はスライスの末尾に位置していると仮定される。
例えば[7, 15, 3, 5, 4, 12, 6]
は述語関数x % 2 != 0
で分割されている(全ての奇数は先頭に、全ての偶数は末尾にある)。
もしスライスが分割されていない場合、このメソッドは二分法の一種を使用するため、戻り値は不定で無意味な値となる。
binary_search
やbinary_search_by
、
またbinary_search_by_key
も参照されたい。
サンプル
let v = [1, 2, 3, 3, 5, 6, 7]; let i = v.partition_point(|&x| x < 5); assert_eq!(i, 4); assert!(v[..i].iter().all(|&x| x < 5)); assert!(v[i..].iter().all(|&x| !(x < 5)));
str::rsplit_once
#[lang = "str"] #[cfg(not(test))] impl str { #[stable(feature = "str_split_once", since = "1.52.0")] #[inline] pub fn rsplit_once<'a, P>(&'a self, delimiter: P) -> Option<(&'a str, &'a str)> where P: Pattern<'a, Searcher: ReverseSearcher<'a>>, { /* 実装は省略 */ } }
文字列を指定された区切りで最後の出現位置で分割し、区切りの前部分と後部分を返す。
サンプル
assert_eq!("cfg".rsplit_once('='), None); assert_eq!("cfg=foo".rsplit_once('='), Some(("cfg", "foo"))); assert_eq!("cfg=foo=bar".rsplit_once('='), Some(("cfg=foo", "bar")));
str::split_once
#[lang = "str"] #[cfg(not(test))] impl str { #[stable(feature = "str_split_once", since = "1.52.0")] #[inline] pub fn split_once<'a, P: Pattern<'a>>(&'a self, delimiter: P) -> Option<(&'a str, &'a str)> { /* 実装は省略 */ } }
文字列を指定された区切りで最初の出現位置で分割し、区切りの前部分と後部分を返す。
サンプル
assert_eq!("cfg".split_once('='), None); assert_eq!("cfg=foo".split_once('='), Some(("cfg", "foo"))); assert_eq!("cfg=foo=bar".split_once('='), Some(("cfg", "foo=bar")));
変更点リスト
言語
- リント
unsafe_op_in_unsafe_fn
を追加した。これはunsafe fn
内で、unsafe
なコードがunsafe
なブロックで囲われているかを確認する。 このリントはデフォルトでは許可されているが、将来のエディションで警告もしくはエラーとなる可能性がある
ピックアップ - 配列への可変参照を、同じ要素型のポインタにキャスト出来るようになった
コンパイラ
以下のターゲットにティア3※サポートを追加した。
s390x-unknown-linux-musl
riscv32gc-unknown-linux-musl
及びriscv64gc-unknown-linux-musl
powerpc-unknown-openbsd
※Rustの、ティアで分けられたプラットフォームの詳細はPlatform Supportのページ(英語)を参照
ライブラリ
OsString
がExtend
とFromIterator
を実装するようになったcmp::Reverse
が#[repr(transparent)]
表現を持つようになったArc<impl Error>
がerror::Error
を実装するようになった- すべての整数型で、除算及び剰余計算が
const
になった
安定化されたAPI
Arguments::as_str
char::MAX
char::REPLACEMENT_CHARACTER
char::UNICODE_VERSION
char::decode_utf16
char::from_digit
char::from_u32_unchecked
char::from_u32
slice::partition_point
str::rsplit_once
str::split_once
また、以前から安定化されていたAPIのうち以下のAPIが定数化された。
char::len_utf8
char::len_utf16
char::to_ascii_uppercase
char::to_ascii_lowercase
char::eq_ignore_ascii_case
u8::to_ascii_uppercase
u8::to_ascii_lowercase
u8::eq_ignore_ascii_case
Rustdoc
- Rustdocのリントがツールのリントとして扱われるようになった。
これにより、Rustdocのリントには(
#[warn(rustdoc::non_autolinks)]
のように)rustdoc::
というプレフィックスが付けられるようになった。 古いスタイルも依然として利用出来るが、将来のリリースで警告が発されるようになる - Rustdocが引数ファイルをサポートした
- Rustdocが、ドキュメント向けにスマート句読点を生成するようになった
- RustdocのMarkdownで「タスクリスト」を使えるようになった。例:
- [x] Complete - [ ] Todo
その他
- テストに複数のフィルタを渡せるようになった。
例:
cargo test -- foo bar
はfoo
とbar
にマッチする全てのテストを実行する - WindowsにおいてRustupが
std
ライブラリのPDBシンボルを配置するようになり、デバッグ時にstd
のシンボルを見られるようになった
内部の変更
これらの変更がユーザーに直接利益をもたらすわけではないものの、コンパイラ及び周辺ツール内部での重要なパフォーマンス改善をもたらす。
- クエリを確認する際、依存グラフの前に結果キャッシュを確認するようになった
- コヒーレンスを確認する際、全てを確認する前にfast_reject::simplify_typeを試すようになった
- いくつかのHIRノードでLocalDefIdのみを格納するようにした
- サイドテーブルでHIR属性を格納するようにした
互換性メモ
- Cargoのビルドスクリプトで
RUSTC_BOOTSTRAP
を設定出来なくなった x86_64-rumprun-netbsd
ターゲットへのサポートを削除したx86_64-pc-solaris
があるためx86_64-sun-solaris
ターゲットを非推奨とした- Rustdocが、コードブロックでの言語指定の区切り文字に
,
と及び
\t
のみ受け入れるようになった - Rustcが
pub_use_of_private_extern_crate
をより多くの場合で捕捉するようになった - 古いバージョンの
proc-macro-hack
において、空白の処理方法が違うことによりパニックを起こす場合がある。 大抵の場合cargo update
を実行するだけで修正出来るはずである
関連リンク
さいごに
次のリリースのRust 1.53は6/17(金)にリリースされる予定です。1.53ではFoo(Bar(x) | Baz(x))
のようなパターンが使えるようになるようです。