1.27.1
稳定版
在此修补
程序前,下例在编译器内部恐慌
.
rust
fn main() {
let a = vec!["".to_string()];
a.iter().enumerate()
.take_while(|(_, &t)| false)
.collect::<Vec<_>>();
}
1.27.1
拒绝上述代码,并显示
以下错误消息:
rust
error[E0507]: cannot move out of borrowed content
--> src/main.rs:4:30
|
4 | .take_while(|(_, &t)| false)
| ^-
| ||
| |hint: to prevent move, use `ref t` or `ref mut t`
| cannot move out of borrowed content
1.27.2
稳定版
rust
fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
match (&t, ()) {
((t,), ()) => t,
}
}
fn main() {
let x = {
let y = Box::new((42,));
transmute_lifetime(&y)
};
println!("{}", x);
}
1.27.2
拒绝上述代码.
1.28.0
稳定版
全局分配器
分配器是Rust
中程序,运行时
从系统取内存
的方式.以前,Rust
禁止改变取内存
方式,这阻止了某些用例.在某些平台上,即使用jemalloc
,在其他平台上,即使用系统
分配器,但用户无法控制此关键组件
.
在1.28.0
中,#[global_allocator]
属性现在是稳定的,它允许Rust
程序设置分配器
为系统
分配器,并通过实现GlobalAlloc
特征来定义
新的分配器.
某些平台上Rust
程序的默认
分配器是jemalloc
.标准库现在提供了需要
时可切换
到的系统分配器
的句柄
,只需要声明为静态
并用#[global_allocator]
属性标记
它.
rust
use std::alloc::System;
#[global_allocator]
static GLOBAL: System = System;//系统
fn main() {
let mut v = Vec::new();
//这使用系统分配器分配内存.
v.push(1);
}
但是,有时想为给定的应用域
自定义分配器.实现GlobalAlloc
特征即可.
改进
了设置格式
的错误消息
仍在诊断
,重点
是格式:
rust
format!("{_foo}", _foo = 6usize);
以前,错误消息
相对较差:
rust
error: invalid format string: expected `'}'`, found `'_'`
|
2 | format!("{_foo}", _foo = 6usize);
| ^^^^^^^^
现在,诊断并告诉格式串
无效的具体原因:
错误:格式串无效:"_foo"
参数名无效
rust
|
2 | let _ = format!("{_foo}", _foo = 6usize);
| ^^^^ invalid argument name in format string
|
= note: argument names cannot start with an underscore
库稳定
另一个
重要稳定性是非零数类型
.这些是标准:NonZeroU8,NonZeroU16,NonZeroU32,NonZeroU64,NonZeroU128
和NonZeroUsize
正整数类型的包装器.
这允许大小优化,如,Option<u8>
有两个字节大,但Option<NonZeroU8>
只有一个字节
大.注意,即使NonZeroU8
在另一个结构中包装
,也有此优化
;
下例说明,尽管在Option
中放置Door
,但仍有1个
字节大.优化
也适合用户定义的枚举:Option
并不特殊.
rust
use std::mem;
use std::num::NonZeroU8;
struct Key(NonZeroU8);
struct Door {
key: Key,
}
fn main() {
assert_eq!(mem::size_of::<Door>(), 1);
assert_eq!(mem::size_of::<Option<Door>>(), 1);
}
Cargo
现在不再允许
发布修改src
目录构建脚本的crate
.crate
中的src
目录应该是不变
的.
1.29.0
稳定版
cargo fix
可自动修复带警告
代码
cargo clippy
是一堆抓常见
错误并改进Rust
代码的lints
.
货物修复
子命令:cargo fix
.如果你以前用Rust
编写过代码,你可能以前见过编译器警告.如,考虑:
rust
fn do_something() {}
fn main() {
for i in 0..100 {
do_something();
}
}
在此,调用了100
次do_something
.但从不使用i
变量.所以Rust
警告说:
rust
cargo build
Compiling myprogram v0.1.0 (file:///path/to/myprogram)
warning: unused variable: `i`
--> src\main.rs:4:9
|
4 | for i in 1..100 {
| ^ help: consider using `_i` instead
|
= note: #[warn(unused_variables)] on by default
Finished dev [unoptimized + debuginfo] target(s) in 0.50s
因此,它建议改用_i
.可自动修复
:
rust
cargo fix
Checking myprogram v0.1.0 (file:///C:/用户/steve/tmp/fix)
Fixing src\main.rs (1 fix)
Finished dev [unoptimized + debuginfo] target(s) in 0.59s
如果再次查看src\main.rs,
会看到代码已更改:
rust
fn do_something() {}
fn main() {
for _i in 0..100 {
do_something();
}
}
现在使用的是_i
,将不再有警告
.
现在可用Rustup
查看cargo clippy
的预览.Clippy
是可针对Rust
代码运行的大量额外警告
.
如:
rust
let mut lock_guard = mutex.lock();
std::mem::drop(&lock_guard)
operation_that_requires_mutex_to_be_unlocked();
此代码
语法上是正确的,但可能有死锁
!你看,删除了lock_guard
的引用,而不是警卫
自身.删除引用
是个空操作,因此这几乎可肯定是个错误
.
可从Rustup
获得Clippy
的预览:
rust
rustup component add clippy-preview
然后运行
它:
rust
cargo clippy
rust
error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
--> src\main.rs:5:5
|
5 | std::mem::drop(&lock_guard);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: #[deny(drop_ref)] on by default
note: argument has type &std::result::Result<std::sync::MutexGuard<'_, i32>, std::sync::PoisonError<std::sync::MutexGuard<'_, i32>>>
--> src\main.rs:5:20
|
5 | std::mem::drop(&lock_guard);
| ^^^^^^^^^^^
= help: for further information visit https://rust-lang-nursery.github.io/rust-clippy/v0.0.212/index.html#drop_ref
从帮助消息
中可看出,可查看clippy
提供的所有lint
.
库稳定
此版本稳定了三个API
:
rust
Arc<T>::downcast
Rc<T>::downcast
Iterator::flatten
此外,现在可比较&str
和OsString
.
除此外,Cargo
现在试修复已被git
合并破坏的锁文件
.可传递--locked
来禁止
此行为.
cargo doc
还增加了一个新标志:--document-private-items
.默认,cargo doc
只文档化公开
内容.
但是,如果在处理
自己的crate
,且有供自己参考的内部文档
,--document-private-items
项为所有
而不仅是公开
项目生成文档.
1.29.1
稳定版
如果传递大数
给str::repeat
,可能导致整数
溢出后的缓冲溢出
.如果不调用str::repeat
函数,则不受影响.
已解决.
1.30.0
稳定版
过程宏
早在Rust1.15
中,就宣布了可定义"自定义继承
"的功能.如,使用serde_derive
,你可
rust
#[derive(Serialize, Deserialize, Debug)]
struct Pet {
name: String,
}
并用serde_json
相互转换Pet
与JSON
,因为serde_derive
过程宏中定义了Serialize
和Deserialize
.
Rust1.30
在此基础上扩展
,增加了定义
另外两个"类属性过程宏"和"类函数过程宏"
,高级宏
的能力.
类似属性
的宏
类似自定义继承宏
,但它们不是仅为#[derive]
属性生成
代码,且允许创建自己的自定义属性
.
也更灵活:继承
仅适合结构和枚举
,但属性
可放在比如函数
等地方.作为使用类似属性的宏
的示例,在使用Web
应用框架时,可能有类似
此的内容:
rust
#[route(GET, "/")]
fn index() {
该#[route]
属性由框架自身按过程宏定义
.签名如下:
rust
#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
在此,有两个输入TokenStreams
:第一个是属性
自身的内容,即GET与"/"
的东西.第二个是属性
附加到的主体
,本例中为fn index(){}
和函数
主体的其余部分.
类似函数
的宏定义
类似调用函数
的宏.如,gnome-class
仓库有个定义Rust
中的GObject
类的过程宏
:
rust
gobject_gen!(
class MyClass: GObject {
foo: Cell<i32>,
bar: RefCell<String>,
}
impl MyClass {
virtual fn my_virtual_method(&self, x: i32) {
//...用X做点什么......
}
}
)
就像以一堆代码
为参数
的函数
.此宏定义如下:
rust
#[proc_macro]
pub fn gobject_gen(input: TokenStream) -> TokenStream {
类似
继承宏的签名
:取括号
内的令牌
,并返回要生成
的代码.
用和宏
现在,可用use
关键字把宏引入
域中.如,要使用serde-json
的json
宏,过去这样:
rust
#[macro_use]
extern crate serde_json;
let john = json!({
"name": "John Doe",
"age": 43,
"phones": [
"+44 1234567",
"+44 2345678"
]
});
但现在,你会这样:
rust
extern crate serde_json;
use serde_json::json;//使用.
let john = json!({
"name": "John Doe",
"age": 43,
"phones": [
"+44 1234567",
"+44 2345678"
]
});
使宏与其他
项目更一致,且无需macro_use
注解.
最后,proc_macro
仓库已稳定,为你提供了编写此类宏期望的API
.还显著
改进了API
错误,且像syn
和quote
等仓库已在使用它们.如,之前:
rust
#[derive(Serialize)]
struct Demo {
ok: String,
bad: std::thread::Thread,
}
给出此错误:
rust
error[E0277]: the trait bound `std::thread::Thread: _IMPL_SERIALIZE_FOR_Demo::_serde::Serialize` is not satisfied
--> src/main.rs:3:10
|
3 | #[derive(Serialize)]
| ^^^^^^^^^ the trait `_IMPL_SERIALIZE_FOR_Demo::_serde::Serialize` is not implemented for `std::thread::Thread`
现在给出:
rust
error[E0277]: the trait bound `std::thread::Thread: serde::Serialize` is not satisfied
--> src/main.rs:7:5
|
7 | bad: std::thread::Thread,
| ^^^ the trait `serde::Serialize` is not implemented for `std::thread::Thread`
改进模块系统
首先是现在预引入外部
仓库,即:
rust
//旧
let json = ::serde_json::from_str("...");
//新增功能
let json = serde_json::from_str("...");
因为Rust
模块,并不总是需要"旧"风格:
rust
extern crate serde_json;
fn main() {
//这很好;在仓库根目录下,所以在此`"serde_json"`在域内
let json = serde_json::from_str("...");
}
mod foo {
fn bar() {
//这不行;在`"foo"`名字空间中,且未在此声明`"serde_json"`
let json = serde_json::from_str("...");
}
//一个选项,是在模块内"用"它
use serde_json;
fn baz() {
//另一个选项是使用`'::serde_json'`,因此用的是`绝对`路径而不是相对路径
let json = ::serde_json::from_str("...");
}
}
移动
函数到子模块
并中断一些代码,并不好.现在,它检查
路径的第一部分,查看它是否是个外部仓库
,如果是,无论模块层次
位置,都可用它.
最后,use
还支持引入crate
开头的带路径项
至域:
rust
mod foo {
pub fn bar() {
//...
}
}
//旧
use ::foo::bar;
//或
use foo::bar;
//新增功能
use crate::foo::bar;
crate
关键字表示想从crate
根目录开始路径.以前,用
后指定路径总是从crate
根目录开始,但直接
引用项目的路径
从本地路径开始,即路径
行为不一致:
rust
mod foo {
pub fn bar() {
//...
}
}
mod baz {
pub fn qux() {
//旧
::foo::bar();
//不管用,这与`"use"`不同:`foo::bar();`
//新增功能
crate::foo::bar();
}
}
把所有这些变化
结合在一起,可更直接
理解路径的解析方式.除了use
语句,一旦看到像a::b::c
此路径,你都可如下:
1,是仓库
名字,然后在里面找b::c
.
2,是crate
关键字,则从crate
的根目录中找b::c
.
3,否则,从模块
当前位置查找a::b::c
.
仍适用,总是从crate
根目录开始
使用路径的旧行为.但是,在切换
到新风格后,给路径
统一应用这些规则
,这样移动代码
时,要调整导入
的次数要少得多.
原始标识
现在,可用一些新语法
,用关键字
作为标识
:
rust
//定义叫`"for"`的局部变量
let r#for = true;
//定义叫`"for"`的函数
fn r#for() {
//...
}
//调用该函数
r#for();
此版本稳定了一些新API
:
rust
Ipv4Addr::{BROADCAST, LOCALHOST, UNSPECIFIED}
Ipv6Addr::{LOCALHOST, UNSPECIFIED}
Iterator::find_map
此外,trim_left
等功能,可消除
某些文本一侧的空白.但是,在考虑从右到左(RTL)
语言时,会混淆"右"和"左"
含义.
因此,给这些API
引入新名:
rust
trim_left -> trim_start
trim_right -> trim_end
trim_left_matches -> trim_start_matches
trim_right_matches -> trim_end_matches
该版本中,Cargo
最大的特点是现在有个进度栏
!