1.66.0
稳定版
显式判定有字段的枚举
即使有字段,带整数表示的枚举
现在也可用显式判定器
.
cpp
#[repr(u8)]
enum Foo {
A(u8),
B(i8),
C(bool) = 42,
}
跨语言
边界传递
值时,在两个
语言中匹配枚举表示
时,显式
判定器非常有用.如
cpp
#[repr(u8)]
enum Bar {
A,
B,
C = 42,
D,
}
这里保证Bar
枚举有与u8
相同的布局.此外,Bar::C
变体保证为42
.
同时,如果只需要不透明
的判定句柄,见std::mem::discriminant
函数.
core::hint::black_box
基准测试或检查
编译器生成的机器代码
时,有时避免
优化很有用.以下示例中,push_cap
函数,在循环中执行了4
次Vec::push
:
cpp
fn push_cap(v: &mut Vec<i32>) {
for i in 0..4 {
v.push(i);
}
}
pub fn bench_push() -> Duration {
let mut v = Vec::with_capacity(4);
let now = Instant::now();
push_cap(&mut v);
now.elapsed()
}
如果在x86_64
上检查编译器的优化输出
,会注意到它很短:
cpp
example::bench_push:
sub rsp, 24
call qword ptr [rip + std::time::Instant::now@GOTPCREL]
lea rdi, [rsp + 8]
mov qword ptr [rsp + 8], rax
mov dword ptr [rsp + 16], edx
call qword ptr [rip + std::time::Instant::elapsed@GOTPCREL]
add rsp, 24
ret
事实上,整个push_cap
函数,都已优化掉
了!
可用新稳定的black_box
函数来解决它.从功能上讲,black_box
不是很有趣:它接受
传递它的值,然后把返回它.
然而,在内部,编译器将black_box
视为,可任意处理输入
并返回值
(即名字)的一个函数
.
它对禁止
优化很有用.如,可向编译器暗示
,在每次for
循环迭代
后,如何用向量
.
cpp
use std::hint::black_box;
fn push_cap(v: &mut Vec<i32>) {
for i in 0..4 {
v.push(i);
black_box(v.as_ptr());
}
}
现在,可在优化
的汇编输出
中找到展开
的for
循环:
cpp
mov dword ptr [rbx], 0
mov qword ptr [rsp + 8], rbx
mov dword ptr [rbx + 4], 1
mov qword ptr [rsp + 8], rbx
mov dword ptr [rbx + 8], 2
mov qword ptr [rsp + 8], rbx
mov dword ptr [rbx + 12], 3
mov qword ptr [rsp + 8], rbx
还可在此汇编输出
中看到调用black_box
的副作用.在每次
迭代后都会无用
地重复mov qword ptr[rsp+8],rbx
指令.
它把v.as_ptr()
地址写入
函数的从未实际调用
的第一个参数
.
注意,生成代码不关心推(push)调用
引入的分配
.这是因为在bench_push
函数中调用Vec::with_capacity(4)
.
可到处试放置black_box
,或在多个位置
试使用它,以查看
它对编译器优化
的影响.
cargo remove
在Rust1.62.0
中,引入了命令行
向项目
添加依赖项
的cargo add
工具.现在,可用cargo remove
来删除依赖项
.
稳定的API
cpp
proc_macro::Span::source_text
u*::{checked_add_signed, overflowing_add_signed, saturating_add_signed, wrapping_add_signed}
i*::{checked_add_unsigned, overflowing_add_unsigned, saturating_add_unsigned, wrapping_add_unsigned}
i*::{checked_sub_unsigned, overflowing_sub_unsigned, saturating_sub_unsigned, wrapping_sub_unsigned}
BTreeSet::{first, last, pop_first, pop_last}
BTreeMap::{first_key_value, last_key_value, first_entry, last_entry, pop_first, pop_last}
在`WASI`上为`stdio`锁类型,添加`AsFd`实现.
impl TryFrom<Vec<T>> for Box<[T; N]>
core::hint::black_box
Duration::try_from_secs_{f32,f64}
Option::unzip
std::os::fd
其他更改
1,现在可在模式
中用..=X
区间.
2,Linux
版本现在分别使用LTO
和Bolt
优化了rustc
前端和LLVM
后端,从而提高了运行时性能和内存使用率
.
1.67.0
稳定版
#[must_use]
在异步函数
上有效
现在,带#[must_use]
注解的异步函数
,应用该属性
至impl Future
返回的输出.Future
特征自身已用#[must_use]
注解,因此所有实现Future
的类型都自动为#[must_use]
.
在1.67
中,编译器现在会在未使用输出
时发出警告
.
cpp
#[must_use]
async fn bar() -> u32 { 0 }
async fn caller() {
bar().await;
}
warning: unused output of future returned by `bar` that must be used
--> src/lib.rs:5:5
|
5 | bar().await;
| ^^^^^^^^^^^
|
= note: `#[warn(unused_must_use)]` on by default
已更新std::sync::mpsc
实现
标准库已有个多生产者,单消费者通道
,但在该版本中,已把实现
切换为基于crossbeam-channel
.
稳定的API
cpp
{integer}::checked_ilog
{integer}::checked_ilog2
{integer}::checked_ilog10
{integer}::ilog
{integer}::ilog2
{integer}::ilog10
NonZeroU*::ilog2
NonZeroU*::ilog10
NonZero*::BITS
这些API
现在在常
环境中是稳定的:
cpp
char::from_u32
char::from_digit
char::to_digit
core::char::from_u32
core::char::from_digit
1.68.0
稳定版
Cargo
的稀疏协议
稳定了Cargo
的"稀疏"注册表协议.
访问crates.io
时,新协议
会显著改进性能
,因为它只下载你实际使用的仓库
子集的信息.
要将稀疏协议
与crates.io
一起使用,请设置环境变量CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse
,或编辑.cargo/config.toml
文件以添加:
cpp
[registries.crates-io]
protocol = "sparse"
本地固定结构
新pin!
宏从T
式以本地状态
匿名抓来构造Pin<&mut T>
.一般叫固定栈,但该"栈"
也可是异步函数
或块
的抓状态
.此宏类似如tokio::pin!
这些仓库
,但标准库可利用Pin
内部结构和临时生命期扩展来得到更像表达式的宏
.
cpp
///完成运行未来.
fn block_on<F: Future>(future: F) -> F::Output {
let waker_that_unparks_thread = todo!();
let mut cx = Context::from_waker(&waker_that_unparks_thread);
//固定未来,以便可轮询它.
let mut pinned_future = pin!(future);
loop {
match pinned_future.as_mut().poll(&mut cx) {
Poll::Pending => thread::park(),
Poll::Ready(result) => return result,
}
}
}
此例中,把原始future
移动到一个临时的局部
,由Pin<&mut F>
类型的新pinned_future
引用它,且该pin
受普通借用检查器
的约束,以确保不会出域
.
默认alloc
错误处理器
Rust
中的分配失败时,像Box::new
和Vec::push
此API
无法指示失败
,因此需要一些不同执行路径.
使用std
仓库时,程序打印到stderr
并中止.从Rust1.68.0
开始,包含std
的二进制
文件继续这样.但在分配失败时,不包括std
,而只包括alloc
的二进制文件
,现在会恐慌
!
如果需要,可通过#[panic_handler]
进一步调整.
将来,也可能会更改std
的行为,以匹配仅分配
的二进制文件的行为.
稳定的API
cpp
{core,std}::pin::pin!
impl From<bool> for {f32,f64}
std::path::MAIN_SEPARATOR_STR
impl DerefMut for PathBuf
这些API
现在在常
环境中是稳定的:
cpp
VecDeque::new
其他更改
如前,Rust
中的安卓
平台支持现在以NDKr25
为目标,这对应于支持的最低API
级别19(KitKat)
.
1.69.0
稳定版
Rust1.69.0
未引入重大的新功能.但是,它包含许多小的改进.
Cargo
现在建议自动修复
一些警告
Rust1.29.0
添加了cargo fix
子命令,可自动修复
一些简单的编译器警告.
此外,还可自动修复一些简单Clippy
警告.
Cargo
建议,检测到可自动修复
的警告
时运行cargo fix
或cargo clippy --fix
:
cpp
warning: unused import: `std::hash::Hash`
--> src/main.rs:1:5
|
1 | use std::hash::Hash;
| ^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
警告:`'foo'(bin"foo")`生成了1个警告(运行`'cargo fix--bin"foo"'`以应用1个建议)
注意,仅当想精确修复时,才需要上面显示的完整Cargo
调用.如果要修复所有,则简单的cargo fix
(无其他参数)就足够了.
默认,构建脚本中不再包含调试信息
为了提高编译速度,Cargo
现在默认在构建脚本
中避免发出调试信息
.只是生成脚本
中的回溯
包含较少的信息.
如果要调试构建脚本
,可添加
以下代码片到Cargo.toml
中,以再次发出调试信息
:
cpp
[profile.dev.build-override]
debug = true
[profile.release.build-override]
debug = true
稳定的API
cpp
CStr::from_bytes_until_nul
core::ffi::FromBytesUntilNulError
可常:
cpp
SocketAddr::new
SocketAddr::ip
SocketAddr::port
SocketAddr::is_ipv4
SocketAddr::is_ipv6
SocketAddrV4::new
SocketAddrV4::ip
SocketAddrV4::port
SocketAddrV6::new
SocketAddrV6::ip
SocketAddrV6::port
SocketAddrV6::flowinfo
SocketAddrV6::scope_id
1.70.0
稳定版
默认,crates.io
为稀疏
如果,要保留
使用由GitHub
管理的git
索引的先前默认值,则可用registries.crates-io.protocol
配置设置来更改默认值
.
OnceCell
和OnceLock
对一次初化
的共享数据
,已稳定
了两个新类型,即OnceCell
及线安版的OnceLock
.可在不需要立即构造
及要求全局常数
时用它.
cpp
use std::sync::OnceLock;
static WINNER: OnceLock<&str> = OnceLock::new();
fn main() {
let winner = std::thread::scope(|s| {
s.spawn(|| WINNER.set("thread"));
std::thread::yield_now(); //给他们一个机会......
WINNER.get_or_init(|| "main")
});
println!("{winner} wins!");
}
过去,像lazy_static
和once_cell
等仓库
已满足了该需求,但现在它们是从once_cell
的unsync
和sync
模块移植而来的标准库
的一部分.
未来要稳定更多,如存储其初化函数
的配套LazyCell
和LazyLock
类型,但当前应已涵盖
了许多用例.
IsTerminal
is_terminal
,确定
给定的文件描述符或句柄
是否表示终端或TTY
.这是另一个(如atty
和is-terminal
的)标准化外部仓库
中存在的功能,在Unix
目标上使用C库的isatty
函数和其他地方的类似函数.
常见的用例是,让程序区分在脚本或交互
模式下运行,如交互
时渲染
颜色甚至完整的TUI
.
cpp
use std::io::{stdout, IsTerminal};
fn main() {
let use_color = stdout().is_terminal();
//请给`程序输出`添加颜色代码...
}
调试信息的名字级别
在测试CLI
中强制稳定性
稳定的API
cpp
NonZero*::MIN/MAX
BinaryHeap::retain
Default for std::collections::binary_heap::IntoIter
Default for std::collections::btree_map::{IntoIter, Iter, IterMut}
Default for std::collections::btree_map::{IntoKeys, Keys}
Default for std::collections::btree_map::{IntoValues, Values}
Default for std::collections::btree_map::Range
Default for std::collections::btree_set::{IntoIter, Iter}
Default for std::collections::btree_set::Range
Default for std::collections::linked_list::{IntoIter, Iter, IterMut}
Default for std::vec::IntoIter
Default for std::iter::Chain
Default for std::iter::Cloned
Default for std::iter::Copied
Default for std::iter::Enumerate
Default for std::iter::Flatten
Default for std::iter::Fuse
Default for std::iter::Rev
Default for std::slice::Iter
Default for std::slice::IterMut
Rc::into_inner
Arc::into_inner
std::cell::OnceCell
Option::is_some_and
NonNull::slice_from_raw_parts
Result::is_ok_and
Result::is_err_and
std::sync::atomic::Atomic*::as_ptr
std::io::IsTerminal
std::os::linux::net::SocketAddrExt
std::os::unix::net::UnixDatagram::bind_addr
std::os::unix::net::UnixDatagram::connect_addr
std::os::unix::net::UnixDatagram::send_to_addr
std::os::unix::net::UnixListener::bind_addr
std::path::Path::as_mut_os_str
std::sync::OnceLock
1.71.0
稳定版
C展开ABI
1.71.0
稳定了C-unwind
(及其他-unwind
后缀的ABI
变体).
1,除了使用-unwind
时,每个ABI
基本上等同于没有-unwind
的相同ABI
,当展开操作(panic
或C++
风格异常)跨越ABI
边界时,则行为
是安全的.
2,对panic=unwind
,这是个有效
方法,可无需终止进程
(只要异常是用相同语言
抓的),让A语言
的异常按B
语言展开
栈;
3,对panic=abort
,这一般会立即中止
进程.
调试器可视化属性
1.71.0
稳定了对新属性的支持,#[debug_visualizer(natvis_file="...")]
和#[debug_visualizer(gdb_script_file="...")]
,
来允许把描述Natvis
和GDB
脚本嵌入到Rust
库中,以在检查这些库
创建的数据结构
时,改进
调试器输出.
Rust
自身已为标准库打包
了一段时间的类似脚本
,但此功能
让库作者享受类似体验
.
raw-dylib
链接
在窗口
平台上,Rust
现在无需构建
时使用这些库,用#[link]
的新kind="raw-dylib"
选项,就支持使用动态库
中的函数.
简化了为窗口
库提供绑定
的仓库
.
Rust
还支持使用新的#[link_ordinal]
属性,通过序号
而不是命名符号
来绑定到DLL
提供的符号
.
升级到musl1.2
如前,Rust1.71
更新musl
版本到1.2.3
.大多数用户不应受到此更改的影响.
常量初化的线本变量
Rust1.59.0
在标准库中稳定了常
初化线本
支持,来允许更优化
的生成代码.
注意,在其他
环境中,该稳定性
不会使const{...}
成为有效的表达式或语法
;这是个单独
的且当前不稳定
的功能.
cpp
use std::cell::Cell;
thread_local! {
pub static FOO: Cell<u32> = const { Cell::new(1) };
}
稳定的API
cpp
CStr::is_empty
BuildHasher::hash_one
NonZeroI*::is_positive
NonZeroI*::is_negative
NonZeroI*::checked_neg
NonZeroI*::overflowing_neg
NonZeroI*::saturating_neg
NonZeroI*::wrapping_neg
Neg for NonZeroI*
Neg for &NonZeroI*
From<[T; N]> for (T...) (对N in 1..=12,array to N-tuple )
From<(T...)> for [T; N] (对N in 1..=12,N-tuple to array )
windows::io::AsHandle for Box<T>
windows::io::AsHandle for Rc<T>
windows::io::AsHandle for Arc<T>
windows::io::AsSocket for Box<T>
windows::io::AsSocket for Rc<T>
windows::io::AsSocket for Arc<T>
这些API
现在在常
环境中是稳定的:
cpp
<*const T>::read
<*const T>::read_unaligned
<*mut T>::read
<*mut T>::read_unaligned
ptr::read
ptr::read_unaligned
<[T]>::split_at
1.72.0
稳定版
Rust
在错误中报告可能有用的cfg
禁止项
可用cfg
有条件地启用Rust
代码,如仅使用某些仓库
功能提供的某些功能
,或仅在指定平台
上提供的某些功能
.
以前,这样禁止
的项目,编译器不可见.不过,现在编译器会记住这些项名
和cfg
条件,因为要启用仓库
功能,可报告(如)你试调用
的函数是否可用.
cpp
Compiling my-project v0.1.0 (/tmp/my-project)
error[E0432]: unresolved import `rustix::io_uring`
--> src/main.rs:1:5
|
1 | use rustix::io_uring;
| ^^^^^^^^^^^^^^^^ no `io_uring` in the root
|
note: found an item that was configured out
--> /home/username/.cargo/registry/src/index.crates.io-6f17d22bba15001f/rustix-0.38.8/src/lib.rs:213:9
|
213 | pub mod io_uring;
| ^^^^^^^^
= note: the item is gated behind the `io_uring` feature
有关此错误的细节,请试`"rustc--explainE0432".`
错误:因为之前的错误,无法编译`"my-project"(bin"my-project")`
常
求值时间现在是无限的
为了避免用户提供的常
计算进入编译时无限循环
,或在编译时
占用无限时间,Rust
之前限制了作为给定常量计算的一部分运行的最大语句数
.
现在,你可在编译时无穷常量计算
.为了长时间无反馈
的编译,会定时报告消息.
默认,编译器还会在执行大量步骤
后发出默认拒绝的lint(const_eval_long_running)
以抓无限循环
,但你可允许(const_eval_long_running)
允许特别长
的常量计算.
从Clippy
提升的检查
在rustc
中,已拉入Clippy
中的几个lint
:
1,clippy::undropped_manually_drops
到undropped_manually_drops
(拒绝)
ManuallyDrop
不会删除
其内部值,因此调用std::mem::drop
会闲着.相反,检查
会先建议ManuallyDrop::into_inner
,或可用不安全的ManuallyDrop::drop
原位运行析构器.
默认,拒绝此lint
.
2,clippy::invalid_utf8_in_unchecked
到invalid_from_utf8_unchecked
(拒绝)和invalid_from_utf8
(警告)
第一个用无效的UTF-8
文本,检查std::str::from_utf8_unchecked
和std::str::from_utf8_unchecked_mut
调用,是否违反了安全前提
,从而导致未定义行为
.
默认,拒绝此lint
.
第二个用无效的UTF-8
文本,调用std::str::from_utf8
和std::str::from_utf8_mut
,检查是否总是返回
错误.默认,此lint
是警告.
3,clippy::cmp_nan
到invalid_nan_comparisons
(警告)
检查f32::NAN
或f64::NAN
的比较.NaN
的这些比较总是错误
的.
默认,此lint
是个警告,并建议改为调用is_nan()
方法.
4,clippy::cast_ref_to_mut
到invalid_reference_casting
(允许)
不用内部可变性
,检查&T
到&mutT
的转换,即使未使用引用
,这也是即时未定义行为
.
稳定的API
cpp
impl<T: Send> Sync for mpsc::Sender<T>
impl TryFrom<&OsStr> for &str
String::leak
这些API
现在在常
环境中是稳定的:
cpp
CStr::from_bytes_with_nul
CStr::to_bytes
CStr::to_bytes_with_nul
CStr::to_str
在1.73.0
稳定版Cleaner
恐慌消息中
已更改默认panic
处理器生成的输出
为在其自己的行上放置panic
消息,而不是用引号
括起来.这样更易阅读紧急消息
,如下例所示:
cpp
fn main() {
let file = "ferris.txt";
panic!("oh no! {file: } not found!");
}
Rust1.73
之前的输出:
cpp
thread 'main' panicked at 'oh no! "ferris.txt" not found!', src/main.rs:3:5
从Rust1.73
开始的输出:
cpp
thread 'main' panicked at src/main.rs:3:5:
oh no! "ferris.txt" not found!
消息很长,包含嵌套引号或跨越多行
时,特别有用.
此外,已修改assert_eq
和assert_ne
生成的紧急消息,移动了自定义消息(第三个参数)
,并删除了一些不必要
的标点符号,如下:
cpp
fn main() {
assert_eq!(" ", " ", "ferris is not a fish");
}
Rust1.73
之前的输出:
cpp
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `" "`,
right: `" "`: ferris is not a fish', src/main.rs:2:5
从Rust1.73
开始的输出:
cpp
thread 'main' panicked at src/main.rs:2:5:
assertion `left == right` failed: ferris is not a fish
left: " "
right: " "
初化线本
正如RFC3184
中所提议的,现在可直接使用get(),set(),take()
和replace()
方法,而不是像对泛型LocalKey
那样通过with(|inner|...)
闭包跳转,来操作LocalKey<Cell<T>>
和LocalKey<RefCell<T>>
.LocalKey<T>
是静态thread_local!
.
新方法
使通用
代码更加简洁,并避免对新线程
,为在thread_local!
中指定的默认值
运行额外初化代码
.
cpp
thread_local! {
static THINGS: Cell<Vec<i32>> = Cell::new(Vec::new());
}
fn f() {
//以前:
THINGS.with(|i| i.set(vec![1, 2, 3]));
//现在:
THINGS.set(vec![1, 2, 3]);
//...
//以前:
let v = THINGS.with(|i| i.take());
//现在:
let v: Vec<i32> = THINGS.take();
}
稳定的API
cpp
Unsigned {integer}::div_ceil
Unsigned {integer}::next_multiple_of
Unsigned {integer}::checked_next_multiple_of
std::ffi::FromBytesUntilNulError
std::os::unix::fs::chown
std::os::unix::fs::fchown
std::os::unix::fs::lchown
LocalKey::<Cell<T>>::get
LocalKey::<Cell<T>>::set
LocalKey::<Cell<T>>::take
LocalKey::<Cell<T>>::replace
LocalKey::<RefCell<T>>::with_borrow
LocalKey::<RefCell<T>>::with_borrow_mut
LocalKey::<RefCell<T>>::set
LocalKey::<RefCell<T>>::take
LocalKey::<RefCell<T>>::replace
这些API
现在在常
环境中是稳定的:
cpp
rc::Weak::new
sync::Weak::new
NonNull::as_ref
1.74.0
稳定版
通过Cargo
配置检查器
正如RFC3389
中所建议的,Cargo.toml
清单现在支持[lints]
表,来配置
编译器和其他工具的检查器
的(禁止,拒绝,警告,允许
)报告级别.
因此,与其使用会影响整个构建
的-F/-D/-W/-A
设置RUSTFLAGS
,你可用仓库
级别的属性,如:
cpp
#![forbid(unsafe_code)]
#![deny(clippy::enum_glob_use)]
现在可在包清单
中编写这些内容,以便Cargo
处理:
cpp
[lints.rust]
unsafe_code = "forbid"
[lints.clippy]
enum_glob_use = "deny"
也可在[workspace.lints]
表中配置它,然后像许多其他工作区
设置一样由[lints]workspace=true
继承.在要决定重建
哪些仓库时,Cargo
还可跟踪
这些设置的更改.
更多信息,见Cargo
参考手册的lints
和workspace.lints
节.
不透明返回类型中的投影
如果曾经
收到过错误"返回类型
不能包含引用父域
生命期的投影
或Self
",现在可高枕无忧了!现在在不透明
的返回类型
中,编译器
允许提及Self
和关联类型
,如async fn
和->impl Trait
.
示例:
cpp
struct Wrapper<'a, T>(&'a T);
//提及`"Self"`的`不透明返回类型`:
impl Wrapper<'_, ()> {
async fn async_fn() -> Self { /*...*/ }
fn impl_trait() -> impl Iterator<Item = Self> { /*...*/ }
}
trait Trait<'a> {
type Assoc;
fn new() -> Self::Assoc;
}
impl Trait<'_> for () {
type Assoc = ();
fn new() {}
}
//提及关联类型的不透明返回类型:
impl<'a, T: Trait<'a>> Wrapper<'a, T> {
async fn mk_assoc() -> T::Assoc { /*...*/ }
fn a_few_assocs() -> impl Iterator<Item = T::Assoc> { /*...*/ }
}
稳定的API
cpp
core::num::Saturating
impl From<io::Stdout> for std::process::Stdio
impl From<io::Stderr> for std::process::Stdio
impl From<OwnedHandle> for std::process::Child{Stdin, Stdout, Stderr}
impl From<OwnedFd> for std::process::Child{Stdin, Stdout, Stderr}
std::ffi::OsString::from_encoded_bytes_unchecked
std::ffi::OsString::into_encoded_bytes
std::ffi::OsStr::from_encoded_bytes_unchecked
std::ffi::OsStr::as_encoded_bytes
std::io::Error::other
impl TryFrom<char> for u16
impl<T: Clone, const N: usize> From<&[T; N]> for Vec<T>
impl<T: Clone, const N: usize> From<&mut [T; N]> for Vec<T>
impl<T, const N: usize> From<[T; N]> for Arc<[T]>
impl<T, const N: usize> From<[T; N]> for Rc<[T]>
这些API
现在在常
环境中是稳定的:
cpp
core::mem::transmute_copy
str::is_ascii
[u8]::is_ascii