2311rust,到66版本更新

1.60.0稳定版

基于源码的代码覆盖率

rustc中已稳定支持基于LLVM的覆盖率检测.可用-Cinstrument-coverage重构代码,如:

RUSTFLAGS="-C instrument-coverage" cargo build

之后,运行生成的二进制文件,它在当前目录中生成一个default.profraw文件.环境变量可覆盖路径和文件名;有关细节,见文档.

llvm-tools-preview组件包括llvm-profdata,来处理和合并原始配置文件输出(覆盖区域执行计数);及用llvm-cov来生成报告.

llvm-cov结合了llvm-profdata二进制输出,因为二进制文件嵌入了从计数器实际源码区域的映射.

rustup component add llvm-tools-preview
$(rustc --print sysroot)/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-profdata merge -sparse default.profraw -o default.profdata
$(rustc --print sysroot)/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-cov show -Xdemangler=rustfilt target/debug/coverage-testing \
    -instr-profile=default.profdata \
    -show-line-counts-or-regions \
    -show-instantiations

在简单的helloworld二进制文件上,执行上述命令会生成此带注释的报告,显示已覆盖每一行输入.

1|  1|fn main() {
2|  1|    println!("Hello, world!");
3|  1|}

细节见rustc书中的文档.
因此,确保llvm-tools-preview和编译代码的rustc使用相同版本.

cargo --timings

Cargo已稳定了对使用--timings标志收集构建信息的支持.

$ cargo build --timings
   Compiling hello-world v0.1.0 (hello-world)
      Timing report saved to target/cargo-timings/cargo-timing-20220318T174818Z.html
    Finished dev [unoptimized + debuginfo] target(s) in 0.98s

Cargo功能的新语法

名字空间依赖和弱依赖.
Cargo长期以来一直支持带可选功能依赖项,如下面代码片所示.

[dependencies]
jpeg-decoder = { version = "0.1.20", default-features = false, optional = true }

[features]
# 并行处理,启用"rayon"特征
parallel = ["jpeg-decoder/rayon"]

此例中,有两点要注意:
1,可选的jpeg-decoder依赖项隐式定义了同名的功能.启用jpeg-decoder功能依赖启用jpeg-decoder项.
2,"jpeg-decoder/rayon"语法启用jpeg-decoder依赖项,并启用jpeg-decoder依赖项的rayon特征.

名字空间功能解决了第一个问题.现在,可在[features]表中无需隐式公开,使用dep:前缀来显式引用可选依赖项.

这样可更好控制如何定义可选依赖项对应的功能,包括在更具描述性功能名后面隐藏依赖可选项.

弱依赖功能解决了第二个问题,即"optional-dependency/feature-name"语法会总是启用可选依赖.但是,一般,仅当其他某些功能已启用可选依赖项时,才想在可选依赖项上启用该功能.
1.60开始,你可像"package-name?/feature-name"一样,添加一个?,只有在其他功能已启用了可选依赖项时,才会启用给定功能.

如,假设在库中支持了一些序化,且需要在一些可选依赖项启用相应的功能.可这样:

[dependencies]
serde = { version = "1.0.133", optional = true }
rgb = { version = "0.8.25", optional = true }
[features]
serde = ["dep:serde", "rgb?/serde"]

此例中,启用serde功能,依赖启用serde项.它还依赖为rgb项启用serde功能,但前提是其他内容启用了rgb依赖项.

稳定的API

Arc::new_cyclic
Rc::new_cyclic
slice::EscapeAscii
<[u8]>::escape_ascii
u8::escape_ascii
Vec::spare_capacity_mut
MaybeUninit::assume_init_drop
MaybeUninit::assume_init_read
i8::abs_diff
i16::abs_diff
i32::abs_diff
i64::abs_diff
i128::abs_diff
isize::abs_diff
u8::abs_diff
u16::abs_diff
u32::abs_diff
u64::abs_diff
u128::abs_diff
usize::abs_diff
Display for io::ErrorKind
From<u8> for ExitCode
Not for ! (the "never" type)
_Op_Assign<$t> for Wrapping<$t>
arch::is_aarch64_feature_detected!

1.61.0稳定版

主的自定义退出码

一开始,Rust主函数只能(隐式或显式)返回单元类型(),总是在退出状态下指示成功,如果想要其他,则必须调用process::exit(code).

Rust1.26开始,允许main返回Result,其中Ok转换为CEXIT_SUCCESS,Err转换为EXIT_FAILURE(也调试打印错误).在后台,由终止特征统一这些替代返回类型.

此版本中,最终用包装了相关平台的返回类型的更通用的ExitCode类型,稳定了终止特征.它有SUCCESSFAILURE常量,且还为更多任意值实现了From.
可为你自己的类型实现终止特征,允许在转换为ExitCode自定义报告类型.
如,下面是为git bisect run脚本编写退出码类型的安全方法:

use std::process::{ExitCode, Termination};
#[repr(u8)]
pub enum GitBisectResult {
    Good = 0,
    Bad = 1,
    Skip = 125,
    Abort = 255,
}
impl Termination for GitBisectResult {
    fn report(self) -> ExitCode {
        //也许在此打印一条消息
        ExitCode::from(self as u8)
    }
}
fn main() -> GitBisectResult {
    std::panic::catch_unwind(|| {
        todo!("test the commit")
    }).unwrap_or(GitBisectResult::Abort)
}

const fn的更多功能

此版本中稳定了几个渐进特征,以在函数中启用更多功能:
1,fn指针的基本处理:现在可在const fn创建,传递和转换函数指针.如,这对解释器构建编译时函数表可能很有用.但是,仍禁止调用fn指针.

2,特征边界:在泛型参数上,现在可把如T:Copy特征边界写入const fn,以前只允许Sized.
3,dyn Trait类型:类似,const fn现在可处理特征对象dyn Trait.
4,impl Trait类型:const fn参数和返回值现在可以是不透明的impl Trait类型.

注意,在const fn中,特征功能尚不支持,从这些特征调用方法.

更多

锁定stdio的静态句柄

三个标准I/O流(Stdin,StdoutStderr)都有一个lock(&self),来更好控制同步读写.然而,他们返回带从&self借用生命期锁警卫,因此仅限于原始句柄的域.

这是不必要的限制,因为底层锁实际在静态存储中,因此现在与句柄断开连接时,返回带"静态生命期"的警卫.

如,常见错误是,试取句柄并在语句锁定它:

//`error[E0716]`:借用时,丢弃了临时值
let out = std::io::stdout().lock();
//^^^^^^^^^^^^^^^^^在此语句末尾释放`临时值`,创建仍在使用时释放了的临时.

现在锁警卫静态的,而不是临时借来的,所以这有效!

稳定的API

Pin::static_mut
Pin::static_ref
Vec::retain_mut
VecDeque::retain_mut
Write for Cursor<[u8; N]>
std::os::unix::net::SocketAddr::from_pathname
std::process::ExitCode
std::process::Termination
std::thread::JoinHandle::is_finished

以下以前稳定的函数现在是:

<*const T>::offset and <*mut T>::offset
<*const T>::wrapping_offset and <*mut T>::wrapping_offset
<*const T>::add and <*mut T>::add
<*const T>::sub and <*mut T>::sub
<*const T>::wrapping_add and <*mut T>::wrapping_add
<*const T>::wrapping_sub and <*mut T>::wrapping_sub
<[T]>::as_mut_ptr
<[T]>::as_ptr_range
<[T]>::as_mut_ptr_range

1.62.0稳定版

cargo add

现在,可用cargo add,直接从命令行添加新的依赖项.支持指定功能和版本.还可用来修改现有依赖项.
如:

cargo add log
cargo add serde --features derive
cargo add nom@5

更多.

#[默认]枚举变体

现在,如果指定默认变体,则可在枚举上使用#[derive(Default)].如,目前,必须手动为此枚举编写默认实现:

#[derive(Default)]
enum Maybe<T> {
    #[default]
    Nothing,
    Something(T),
}

目前,只允许标记"单位"变体(无字段变体)为#[default].细节.

Linux上更薄,更快的互斥锁

以前,由Linux上的pthreads库,支持Mutex,CondvarRwLock.pthreads锁支持比RustAPI自身更多的功能,包括运行时配置,且可用于静态保证比Rust更少的语言.

如,按无法移动的40个字节实现互斥锁.这强制标准库在后台为使用pthreads的平台的每个新互斥锁分配一个Box.

现在在Linux上,Rust的标准库提供了原始基于futex实现的,它非常轻量,不需要额外分配.

1.62.0中,在Linux上的内部状态中,互斥锁只需要5个字节,更多.

裸金属x86_64目标

现在,为x86_64构建无操作系统二进制文件更加容易,如在编写内核时.x86_64-unknown-none目标已提升到第2级,可用rustup安装.

rustup target add x86_64-unknown-none
rustc --target x86_64-unknown-none my_no_std_program.rs

嵌入式的rust

稳定的API

bool::then_some
f32::total_cmp
f64::total_cmp
Stdin::lines
windows::CommandExt::raw_arg
impl<T: Default> Default for AssertUnwindSafe<T>
From<Rc<str>> for Rc<[u8]>
From<Arc<str>> for Arc<[u8]>
FusedIterator for EncodeWide

aarch64上的RDM内部函数,这里

1.62.1稳定版

1,编译器修复了涉及impl Trait返回类型的不健全函数强制.
2,编译器修复了异步 fn生命期的增量编译错误.
3,窗口同步读写中的重叠I/O添加了回退.

1.63.0稳定版

域线程

1.0开始,Rust代码可用std::thread::spawn启动新线程,但此函数要用'static绑定闭包.即,即线程当前必须有传递到闭包中的参数的所有权;

不能把借用数据传递到线程中.如果(通过join()),应该在结束函数时,退出线程,这不必要,可能需要在Arc中放置数据等变通方法.

现在,在1.63.0中,标准库添加了允许生成从本地栈帧中借用线程域线程.std::thread::scope,API提供,即在返回前,生成线程都已退出,从而安全借用数据的必要保证.
下面是一例:

let mut a = vec![1, 2, 3];
let mut x = 0;
std::thread::scope(|s| {
    s.spawn(|| {
        println!("hello from the first scoped thread");
        //可在此借用`"a"`.
        dbg!(&a);
    });
    s.spawn(|| {
        println!("hello from the second scoped thread");
        //甚至可在此可变借用`"x"`,因为没有其他线程使用它.
        x += a[0] + a[2];
    });
    println!("hello from the main thread");
});
//在域后,可再次`修改和访问`变量:
a.push(4);
assert_eq!(x, a.len());

(I/O安全)原始文件描述符/句柄的Rust所有权

以前,(在unix风格平台上)使用带原始文件描述符或(在窗口上)句柄的平台APIRust代码,一般直接使用相关平台的描述符表示(如,c_int或别名RawFd).
对与此类原生APIRust绑定,类型系统无法编码API是否取得文件描述符的所有权(如,close)还是仅借用它(如,dup).

现在,Rust提供了按#[repr(transparent)]标记的BorrowedFdOwnedFd等包装器类型,即外部"C"绑定可直接用它们编码所有权语义.

建议新的API使用它们,而不是以前的(如RawFd)类型别名.

初化const互斥锁,RwLock,Condvar

现在可在环境中调用Condvar::new,Mutex::newRwLock::new函数,这样可避免使用像lazy_static仓库来创建互斥(Mutex),RwLockCondvar全局静态值.

带impl Trait的鱼泛型

对像fn foo(value:T,f:impl Copy)类函数签名,通过指定T具体类型是错误的:foo::(3,3)将失败,并显示:

error[E0632]: cannot provide explicit generic arguments when `impl Trait` is used in argument position
 --> src/lib.rs:4:11
  |
4 |     foo::<u32>(3, 3);
  |           ^^^ explicit generic argument not allowed
  |
  = note: see issue #83701 <https://github.com/rust-lang/rust/issues/83701> for more information

1.63中,放宽了此限制,且可指定泛型显式类型.但是,impl Trait参数尽管是泛型,但仍是不透明的,无法通过指定.

稳定的API

array::from_fn
Box::into_pin
BinaryHeap::try_reserve
BinaryHeap::try_reserve_exact
OsString::try_reserve
OsString::try_reserve_exact
PathBuf::try_reserve
PathBuf::try_reserve_exact
Path::try_exists
Ref::filter_map
RefMut::filter_map
NonNull::<[T]>::len
ToOwned::clone_into
Ipv6Addr::to_ipv4_mapped
unix::io::AsFd
unix::io::BorrowedFd<'fd>
unix::io::OwnedFd
windows::io::AsHandle
windows::io::BorrowedHandle<'handle>
windows::io::OwnedHandle
windows::io::HandleOrInvalid
windows::io::HandleOrNull
windows::io::InvalidHandleError
windows::io::NullHandleError
windows::io::AsSocket
windows::io::BorrowedSocket<'handle>
windows::io::OwnedSocket
thread::scope
thread::Scope
thread::ScopedJoinHandle

这些API现在可在环境中使用:

array::from_ref
slice::from_ref
intrinsics::copy
intrinsics::copy_nonoverlapping
<*const T>::copy_to
<*const T>::copy_to_nonoverlapping
<*mut T>::copy_to
<*mut T>::copy_to_nonoverlapping
<*mut T>::copy_from
<*mut T>::copy_from_nonoverlapping
str::from_utf8
Utf8Error::error_len
Utf8Error::valid_up_to
Condvar::new
Mutex::new
RwLock::new

1.64.0稳定版

IntoFuture增强.await

Rust1.64稳定了,类似IntoIterator的特征,但不是支持for循环,而是改变.await的工作方式的IntoFuture特性.

使用IntoFuture,.await关键字不仅可等待未来;还可等待可通过IntoFuture转换为Future的任意东西,帮助API更加用户友好!
如,构造通过网络请求某个存储提供者构建器:

pub struct Error { ... }
pub struct StorageResponse { ... }:
pub struct StorageRequest(bool);
impl StorageRequest {
    ///创建`"StorageRequest"`的新实例.
    pub fn new() -> Self { ... }
    ///决定是否应启用调试模式.
    pub fn set_debug(self, b: bool) -> Self { ... }
    ///发送请求并接收响应.
    pub async fn send(self) -> Result<StorageResponse, Error> { ... }
}

典型用法可能如下:

let response = StorageRequest::new()  //`1`.创建新实例
    .set_debug(true)//`2`.设置一些选项
    .send()         //`3`.构建未来
    .await?;        //`4`.运行未来+传播错误

这还不错,但在此还可更好.使用IntoFuture,可把构建"未来"(第3行)和"运行未来"(第4行)合并为一个步骤:

let response = StorageRequest::new()  //`1`.创建新实例
    .set_debug(true)    //`2`设置一些选项
    .await?;//`3`.构造+运行未来+传播错误

为此,可为StorageRequest实现IntoFuture.IntoFuture要求有个可创建"盒子<未来>"并为其定义一个类型别名来完成的可返回的命名未来:

//首先,必须导入一些新类型到域中.
use std::pin::Pin;
use std::future::{Future, IntoFuture};
pub struct Error { ... }
pub struct StorageResponse { ... }
pub struct StorageRequest(bool);
impl StorageRequest {
    ///创建`"StorageRequest"`的新实例.
    pub fn new() -> Self { ... }
    ///决定是否应启用调试模式.
    pub fn set_debug(self, b: bool) -> Self { ... }
    ///发送请求并接收响应.
    pub async fn send(self) -> Result<StorageResponse, Error> { ... }
}
//新的实现:`1`.创建新的`命名未来`类型
//`2`.为`"StorageRequest"`实现`"IntoFuture"`
pub type StorageRequestFuture = Pin<Box<dyn Future<Output = Result<StorageResponse, Error>> + Send + 'static>>
impl IntoFuture for StorageRequest {
    type IntoFuture = StorageRequestFuture;
    type Output = <StorageRequestFuture as Future>::Output;
    fn into_future(self) -> Self::IntoFuture {
        Box::pin(self.send())
    }
}

更多实现代码,但给用户提供了更简单API.
未来,Rust异步工作组想通过在类型(type)别名(类型别名实现特征或TAIT)中支持impl Trait简化创建新的命名未来.
通过简化类型别名的签名,来简化实现IntoFuture的过程,并从类型别名中删除Box提高性能.

核心和分配中与C兼容的FFI类型

调用或被CABI调用时,Rust代码可无需相关目标代码或条件,用(如c_uintc_ulong)类型别名来匹配目标上C的相应类型.
以前,仅在std中可用分类名,因此为嵌入目标和其他只能使用corealloc的场景,编写的代码不能用类型别名.

Rust1.64现在提供了core::ffi中的所有c_*类型别名,及处理C串core::ffi::CStr.Rust1.64还提供了alloc::ffi::CString,来仅使用alloc仓库而不是完整的std库来处理拥有的C串.

现在可通过rustup取得rust-analyzer

rust-analyzer这里,现在包含在Rust附带的工具集合中.这样更容易下载和访问rust-analyzer,并在多平台上可用.可作为rustup组件使用,如下安装:

rustup component add rust-analyzer

此时,要运行rustup安装的版本,这样调用它:

rustup run stable rust-analyzer

rustup的下个版本提供内置代理,以便运行可执行的rust-analyzer会启动适当版本.
发布,vsc插件

改进Cargo:工作区继承和多目标构建

现在,在一个Cargo工作区中,使用相关库或二进制仓库的集合时,可避免在仓库之间重复通用字段值,如通用版本号,仓库URLrust-version,及从工作区继承依赖项.
帮助更新仓库时保持这些值间同步.

在为多个目标构建时,现在可传递多个--target选项给cargo build,以一次构建所有这些目标.还可在.cargo/config.toml中,将build.target设置为包含多个目标的数组,以便默认为多个目标构建.

稳定的API

future::IntoFuture
num::NonZero*::checked_mul
num::NonZero*::checked_pow
num::NonZero*::saturating_mul
num::NonZero*::saturating_pow
num::NonZeroI*::abs
num::NonZeroI*::checked_abs
num::NonZeroI*::overflowing_abs
num::NonZeroI*::saturating_abs
num::NonZeroI*::unsigned_abs
num::NonZeroI*::wrapping_abs
num::NonZeroU*::checked_add
num::NonZeroU*::checked_next_power_of_two
num::NonZeroU*::saturating_add
os::unix::process::CommandExt::process_group
os::windows::fs::FileTypeExt::is_symlink_dir
os::windows::fs::FileTypeExt::is_symlink_file

以前在std::ffi中是稳定的,但现在在corealloc中也可用:

core::ffi::CStr
core::ffi::FromBytesWithNulError
alloc::ffi::CString
alloc::ffi::FromVecWithNulError
alloc::ffi::IntoStringError
alloc::ffi::NulError

以前在std::os::raw中是稳定的,但现在在core::ffistd::ffi中也可用:

ffi::c_char
ffi::c_double
ffi::c_float
ffi::c_int
ffi::c_long
ffi::c_longlong
ffi::c_schar
ffi::c_short
ffi::c_uchar
ffi::c_uint
ffi::c_ulong
ffi::c_ulonglong
ffi::c_ushort

已稳定了一些来future下面低级实现的Poll助手:

future::poll_fn
task::ready!

将来,可能会提供更简单的较少低级细节的,如PollPinAPI,但同时,这些助手使得更加容易编写此类代码.
现在可在环境中使用这些API:

slice::from_raw_parts

Rust1.64.0更改了Ipv4Addr,Ipv6Addr,SocketAddrV4SocketAddrV6内存布局,使其更加紧凑和内存高效.

1.65.0稳定版

(GAT)泛型关联类型

现在可在关联类型上定义生命期,类型和常量泛型,如下:

trait Foo {
    type Bar<'x>;
}

这里有些示例特征,以了解它们的强大:

///可从"Self"中借用的,类似"迭代器"的特征
trait LendingIterator {
    type Item<'a> where Self: 'a;
    fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
}
///可通过(如`"Rc"`或`"Arc"`)灵针实现,以便允许在指针类型上泛型

trait PointerFamily {
    type Pointer<T>: Deref<Target = T>;
    fn new<T>(value: T) -> Self::Pointer<T>;
}
///允许借用`项目数组`.适合类似`'NdArray'`的不必连续`存储`数据的类型.
trait BorrowArray<T> {
    type Array<'x, const N: usize> where Self: 'x;
    fn borrow_array<'a, const N: usize>(&'a self) -> Self::Array<'a, N>;
}

let-else语句

引入了一个新的,带可反驳的模式和发散的不匹配模式时执行的else块的let型语句.

let PATTERN: TYPE = EXPRESSION else {
    DIVERGING_CODE;
};

普通let语句只能使用无可反驳模式,即静态已知总是匹配.该模式一般只是单个变量绑定,但也可解包如结构,元组和数组复合类型.

但是,这不适合如提取枚举变体的条件匹配,但现在使用let-else,可像普通let一样匹配绑定域内变量的可反驳的模式,或在(如break,return,panic!)模式不匹配时发散.

fn get_count_item(s: &str) -> (u64, &str) {
    let mut it = s.split(' ');
    let (Some(count_str), Some(item)) = (it.next(), it.next()) else {
        panic!("Can't segment count item pair: '{s}'");
    };
    let Ok(count) = u64::from_str(count_str) else {
        panic!("Can't parse integer: '{count_str}'");
    };
    (count, item)
}
assert_eq!(get_count_item("3 chairs"), (3, "chairs"));

名字绑定的域,是使其与matchiflet-else式不同的主要因素.以前,可通过一些重复和外部的来模拟这些模式:

let (count_str, item) = match (it.next(), it.next()) {
    (Some(count_str), Some(item)) => (count_str, item),
    _ => panic!("Can't segment count item pair: '{s}'"),
};
let count = if let Ok(count) = u64::from_str(count_str) {
    count
} else {
    panic!("不能解析整:{count_str}'");
};

从标签块中断开

现在可按中断(break)目标标记普通块式,来提前终止该块.有点类似goto语句,但它不是goto语句,只是从块内跳到块尾.
这在循环块中已是可能的,你可能会看到总是只执行一次的循环,只是为了得到有标签中断.

现在有个专门为此的语言功能!与循环一样,标签break也可包含式值,让多语句块有早期的"返回"值.

let result = 'block: {
    do_thing();
    if condition_not_met() {
        break 'block 1;
    }
    do_next_thing();
    if condition_not_met() {
        break 'block 2;
    }
    do_last_thing();
    3
};

拆分Linux调试信息

早在Rust1.51中,编译器团队在macOS上添加了对拆分调试信息的支持,现在在Linux上也可稳定使用该选项.

1,-Csplit-debuginfo=unpacked会将调试信息拆分为多个.dwo,DWARF目标文件.
2,-Csplit-debuginfo=packed除了输出二进制文件,还生成一个打包所有调试信息在一起的.dwpDWARF文件.
3,-Csplit-debuginfo=off仍是默认,在目标和最终二进制文件中的.debug_*ELF节中包括DWARF数据.
拆分DWARF允许链接器避免处理调试信息(因为它不再在链接目标文件中),这可加快链接时间!

对相关平台的默认值,其他目标现在也接受-Csplit-debuginfo作为稳定选项,但其他值仍不稳定.

稳定的API

std::backtrace::Backtrace
Bound::as_ref
std::io::read_to_string
<*const T>::cast_mut
<*mut T>::cast_const

特别注意,Backtrace,API允许使用一般恐慌回溯的相同平台相关的实现,来随时抓栈回溯.如,这对向错误类型添加运行时环境可能很有用.
这些API现在可在环境中使用:

<*const T>::offset_from
<*mut T>::offset_from

其他更改

Rust1.65版本中还有其他更改,包括:
现在启用了MIR内联以优化编译.
调度构建时,Cargo现在会排序等待处理作业的队列,以提高性能.

你可能感兴趣的:(rust,rust)