1.23
从Rust1.0
开始,有叫AsciiExt
的特征
来提供u8,char,[u8]
和str
上的ASCII
相关功能.要使用它,需要如下编写
代码:
cpp
use std::ascii::AsciiExt;
let ascii = 'a';
let non_ascii = ' ';
let int_ascii = 97;
assert!(ascii.is_ascii());
assert!(!non_ascii.is_ascii());
assert!(int_ascii.is_ascii());
在Rust1.23
中,现在直接在这些类上定义这些方法
,因此不再需要导入trait
.
cpp
#[allow(unused_imports)]
use std::ascii::AsciiExt;
//也可继续以前导入.
来抑制
相关警告.
此外,新API
:
现在从非原子类型实现
各种std::sync::atomic
类型.如,
cpp
let x = AtomicBool::from(true);
()
现在实现了FromIterator<()>
;
RwLock<T>
已取消
发送限制
货物特点
1,Cargo Check
现在可检查你的单元测试
.
2,cargo uninstall
现在可在一个命令
中卸载
多个包.
1.24
cpp
rustup component add rustfmt-preview
//格式组件.
有两点:首先,在此使用的是
cpp
rustup component add
//而不是
cargo install
如果以前通过cargo install
使用过rustfmt
,应该先卸载
它.
其次,这是一个预览.
增量编译
从Rust1.24
开始,默认增量编译
.
要在Cargo.toml
中设置codegen-units
为1(或16)
,以提升每一滴性能.
未定义行为.Rust
一般努力减少未定义行为,在安全代码中没有未定义行为,在不安全代码中尽量少.
跨越FFI
边界,恐慌(panic)!
时,可调用UB
.即:
cpp
extern "C" fn panic_in_ffi() {
panic!("Test");
}
在Rust1.24
中,代码
现在中止
,而不是ub
.
str::find
,在&str
中查找给定的符:它现在快了10
倍!这要归功于memchr
.[u8]::contains
也用它.
此外,还稳定了一些新API
:
cpp
RefCell::replace
RefCell::swap
std::sync::atomic::spin_loop_hint
最后,现在可在常量式
中使用这些函数
,如,初化静
:
1,Cell,RefCell
和UnsafeCell
的新功能
2,各种原子
整数类型的新函数
3,{integer}::min_value
和max_value
4,MEM
的size_of
和align_of
5,ptr::null
和null_mut
1.24.1
稳定版
更改细节:
1,通过FFI
展开时不会中止(恢复1.24.0
中添加的行为)
2,在窗口
上为链接器
参数发出UTF-16
文件
3,使错误索引
生成器再次工作
4,如果要更新,Cargo
在窗口7
上发出警告.
rlua
的异常.
深入
挖掘后,发现了原因:setjmp/longjmp
.由C标准库提供这些函数
来处理错误.先调用setjmp
,然后稍后再调用longjmp
.
这样,控制流
返回到之前调用setjmp
的位置.它一般用来实现异常
,甚至协程.Lua
的实现使用setjmp/longjmp
来实现异常:
与C++
或Java
不同,C
语言不提供异常
处理机制.为了改善,Lua
使用了C语言中的setjmp
工具,这导致了类似异常处理
的机制.
如果用C++
编译Lua
,则更改代码
来使用真正的异常并不困难.
所以,问题变成了:为什么会在窗口
上破裂
窗口
有个叫"结构化异常处理
"(SEH
)的概念.窗口
使用SEH
来实现setjmp/longjmp
,因为SEH
的整个思想
是有统一的错误处理.
类似,C++
异常使用SEH
,Rust
恐慌也是.
看看rlua
的工作原理.rlua
有个protect_lua_call
内部函数,来调用Lua
.它像这样使用
:
cpp
protect_lua_call(self.state, 0, 1, |state| {
ffi::lua_newtable(state);
}) ;
即,protect_lua_call
需要一些包含闭包
的参数.把此闭包
传递给lua_pcall
,它再抓传递
给它的代码(该闭包
)可能抛的longjmp
.
即,代码
现在像如下伪码
:
cpp
protect_lua_call(self.state, 0, 1, |state| {
let result = panic::catch_unwind(|| {
ffi::lua_newtable(state);
});
if result.is_err() {
process::abort();
}
}) ;
之前,在讨论setjmp/longjmp
时,说过在Rust
代码中的问题是,它不处理Rust
析构器.因此,在除窗口
之外的所有平台上,上述catch_unwind
恶作剧都被有效地忽略了,因此一切正常.
但是,在窗口
上,因为setjmp/longjmp
和Rust
的panic
都使用SEH
,因此抓住了longjmp
,并运行
新的中止
代码!
1.25.0
稳定版
已从LLVM4
升级到了LLVM6
.
嵌套
导入组.
cpp
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
现在可这样写:
cpp
//在一行上
use std::{fs::File, io::Read, path::{Path, PathBuf}};
//更多空间
use std::{
fs::File,
io::Read,
path::{
Path,
PathBuf
}
};
接受允许设置
结构对齐方式属性
的如下对齐:
cpp
#[repr(align(x))]
示例:
cpp
struct Number(i32);
assert_eq!(std::mem::align_of::<Number>(), 4);
assert_eq!(std::mem::size_of::<Number>(), 4);
#[repr(align(16))]
struct Align16(i32);
assert_eq!(std::mem::align_of::<Align16>(), 16);
assert_eq!(std::mem::size_of::<Align16>(), 16);
1,std::ptr::NonNull<T>
.此类型类似*mut T
,但为非null
且协变.NonNull<T>
保证它不会为null
,即Option<NonNull<T>>
的大小与*mut T
相同.
如果要用不安全
代码构建
数据结构,NonNull<T>
适合你!
2,libcore
获得了,包含以前仅在libstd
中可用的Duration
类型的time
模块.
此外,与Duration
关联的from_secs
和from_millis
函数都变成了常量函数
,允许按常量式创建Duration
.
3,:cargo new
现在默认生成二进制文件
,而不是库
.可用--lib/--bin
来设置.
1.26.0
稳定版
impl
特征
impl Trait
来了!提供了叫"存在类型"
的功能.核心想法是:
cpp
fn foo() -> impl Trait {
//...
}
此类型签名
,表示"foo
是一个无参
但返回实现Trait
特征的类型"的函数.也即,不会告诉你foo
的具体返回类型
,只告诉你它实现了指定特征
.
与特征对象
有何不同?
cpp
fn foo() -> Box<Trait> {
//...
}
假设有个针对i32
和f32
实现的Trait
特征:
cpp
trait Trait {
fn method(&self);
}
impl Trait for i32 {
//实现在此
}
impl Trait for f32 {
//实现在此
}
考虑以下函数:
cpp
fn foo() -> ? {
5
}
如何填充
返回类型.以前,只能用特征对象
:
cpp
fn foo() -> Box<Trait> {
Box::new(5) as Box<Trait>
}
但这引入了要分配
的Box
.在此也没有
返回动态数据
,所以也会影响特征
对象的动态调度
.因此,从Rust1.26
开始,可这样写:
cpp
fn foo() -> impl Trait {
5
}
这不会创建
,特征
对象,与写了-> i32
一样,但,只提及Traits
部分.从而得到静态调度
,但隐藏
了真实类型.
闭包
中有用.Rust
中闭包
都有个唯一
的,不可写的但实现了Fn
特征的类型
.即,如果函数
返回闭包
,可如下:
cpp
//以前
fn foo() -> Box<Fn(i32) -> i32> {
Box::new(|x| x + 1)
}
//后
fn foo() -> impl Fn(i32) -> i32 {
|x| x + 1
}
没有装箱(Box)
,没有动态调度
.返回
迭代器类似.迭代器不仅一般甚至包含嵌套闭包
.如:
cpp
fn foo() {
vec![1, 2, 3]
.into_iter()
.map(|x| x + 1)
.filter(|x| x % 2 == 0)
}
编译时,报错:
cpp
error[E0308]: mismatched types
--> src/main.rs:5:5
|
5 | / vec![1, 2, 3]
6 | | .into_iter()
7 | | .map(|x| x + 1)
8 | | .filter(|x| x % 2 == 0)
| |_______________________________^ expected (), found struct `std::iter::Filter`
|
= note: expected type `()`
found type `std::iter::Filter<std::iter::Map<std::vec::IntoIter<{integer}>, [closure@src/main.
rs:7:14: 7:23]>, [closure@src/main.rs:8:17: 8:31]>`
以前,必须在此
使用特征对象
,但现在可简单
这样:
cpp
fn foo() -> impl Iterator<Item = i32> {
vec![1, 2, 3]
.into_iter()
.map(|x| x + 1)
.filter(|x| x % 2 == 0)
}
并完成
它.同未来
工作类似.
注意,有时仍需要特征对象
.仅当函数
返回单个类型
时,才能使用impl Trait
;如果要返回
多个,则需要动态调度.如:
cpp
fn foo(x: i32) -> Box<Iterator<Item = i32>> {
let iter = vec![1, 2, 3]
.into_iter()
.map(|x| x + 1);
if x % 2 == 0 {
Box::new(iter.filter(|x| x % 2 == 0))
} else {
Box::new(iter)
}
}
为了使语法
更对称,也可在参数位置
使用impl Traits
:
cpp
//以前
fn foo<T: Trait>(x: T) {
//以后
fn foo(x: impl Trait) {
更好的匹配绑定
你是否曾经引用过Option
,并试使用match
?如,如下代码
:
cpp
fn hello(arg: &Option<String>) {
match arg {
Some(name) => println!("Hello {}!", name),
None => println!("你是谁?"),
}
}
如果试在Rust1.25
中编译它,会得到错误:
cpp
error[E0658]: non-reference pattern used to match a reference (see issue #42640)
--> src/main.rs:6:9
|
6 | Some(name) => println!("Hello {}!", name),
| ^^^^^^^^^^ help: consider using a reference: `&Some(name)`
error[E0658]: non-reference pattern used to match a reference (see issue #42640)
--> src/main.rs:7:9
|
7 | None => println!("I don't know who you are."),
| ^^^^ help: consider using a reference: `&None`
好的,当然.修改代码:
cpp
fn hello(arg: &Option<String>) {
match arg {
&Some(name) => println!("Hello {}!", name),
&None => println!("你是谁?"),
}
}
添加了编译器抱怨的&s
.再次试编译:
cpp
错误`[E0507]`:无法移出借用的内容
--> src/main.rs:6:9
|
6 | &Some(name) => println!("Hello {}!", name),
| ^^^^^^----^
| | |
| | hint: to prevent move, use `ref name` or `ref mut name`
|无法移出借用的内容
好的如下:
cpp
fn hello(arg: &Option<String>) {
match arg {
&Some(ref name) => println!("Hello {}!", name),
&None => println!("你是谁?"),
}
}
不得不添加两个&s
和一个ref
.
从Rust1.26
开始,没有&s
和ref
的初始代码
,完全按你的期望编译
和执行
.总之,在match
语句中自动引用或解引用
.所以
cpp
match arg {
Some(name) => println!("Hello {}!", name),
编译器会自动引用Some
,且因为借用了name
,因此name
也自动绑定为ref name
.如果修改为:
cpp
fn hello(arg: &mut Option<String>) {
match arg {
Some(name) => name.push_str(", world"),
None => (),
}
}
编译器自动按可变引用
借用,且name
也按ref mut
绑定.
main
也可返回结果
cpp
use std::fs::File;
fn main() {
let f = File::open("bar.txt")?;
}
导致了如下编写代码:
cpp
fn run(config: Config) -> Result<(), Box<Error>> {
//...
}
fn main() {
//...
if let Err(e) = run(config) {
println!("Application error: {}", e);
process::exit(1);
}
}
在Rust1.26
中,现在可声明返回Result
的main
:
cpp
use std::fs::File;
fn main() -> Result<(), std::io::Error> {
let f = File::open("bar.txt") ;
Ok(())
}
现在正常工作
!如果main
返回错误,则退出
并显示错误码,并打印出
错误的调试表示.
包含...=
可如下使用..
:
cpp
for i in 1..3 {
println!("i: {}", i);
}
这打印i:1,
然后打印i:2
.在Rust1.26
中,你现在可如下创建包含区间
:
cpp
for i in 1..=3 {
println!("i: {}", i);
}
这打印如前i:1
,然后打印i:2
,但也打印i:3
.
cpp
fn takes_u8(x: u8) {
//...
}
fn main() {
for i in 0..256 {
println!("i: {}", i);
takes_u8(i);
}
}
编译时
收到提示:
警告:U8
文本出域
cpp
--> src/main.rs:6:17
|
6 | for i in 0..256 {
| ^^^
|
= note: #[warn(overflowing_literals)] on by default
但是,可用包含区间
来完成:
cpp
fn takes_u8(x: u8) {
//...
}
fn main() {
for i in 0..=255 {
println!("i: {}", i);
takes_u8(i);
}
}
产生你可能期望的256
行输出.
基本切片模式
允许在切片
上匹配.如:
cpp
let arr = [1, 2, 3];
match arr {
[1, _, _] => "starts with one",
[a, b, c] => "starts with something else",
}
在本例中,知道arr
的长度为3
,因此需要在[]
中加入3个项.不知道长度
时,也可匹配
:
cpp
fn foo(s: &[u8]) {
match s {
[a, b] => (),
[a, b, c] => (),
_ => (),
}
}
128
位整数,Rust
现在有128
位整数!
cpp
let x: i128 = 0;
let y: u128 = 0;
大小是u64
的两倍,因此可容纳更多值
.即,
cpp
u128: 0 - 340,282,366,920,938,463,463,374,607,431,768,211,455
i128: 170,141,183,460,469,231,731,687,303,715,884,105,728 - 170,141,183,460,469,231,731,687,303,715,884,105,727
库稳定
稳定了比File::open
和io::Read::read_to_string
更方便的fs::read_to_string
,它可轻松地一次读取整个文件
到内存
中:
cpp
use std::fs;
use std::net::SocketAddr;
let foo: SocketAddr = fs::read_to_string("address.txt")?.parse()?;
现在,你可用Debug
格式将数字格式化为十六进制:
cpp
assert!(format!("{:02x?}", b"Foo\0") == "[46, 6f, 6f, 00]")
标准库中的所有宏
现在都支持尾随逗号
.
1.26.2
稳定版
漏洞:允许同时对路径
有两个可变
借用.
cpp
let mut foo = Some("foo".to_string());
let bar = &mut foo;
match bar {
Some(baz) => {
bar.take(); //不应允许,因为`baz`唯一引用`条形指针`.
},
None => unreachable!(),
}
1.26.2
拒绝上述代码,并显示
以下错误消息:
cpp
error[E0499]: cannot borrow `*bar` as mutable more than once at a time
--> src/main.rs:6:9
|
5 | Some(baz) => {
| --- first mutable borrow occurs here
6 | bar.take(); //不应被允许,因为`baz`有一个......
| ^^^ second mutable borrow occurs here
...
9 | }
|第一次借用到此结束
错误:因为上一个错误而中止
1.27.0
稳定版
SIMD
好了,现在是大新闻:现已发布SIMD
基础!SIMD
代表"单指令,多数据
".考虑如下此函数:
cpp
pub fn foo(a: &[u8], b: &[u8], c: &mut [u8]) {
for ((a, b), c) in a.iter().zip(b).zip(c) {
*c = *a + *b;
}
}
在此,取两个切片
,并把数字
相加,在第三个切片中放置结果
.
但是,编译器可做得更好.LLVM
一般会"自动向量化
"代码.
在Rust1.27
中,添加了std::arch
模块,允许直接
使用这类指令,不必
依赖编译器.此外,还包括一些允许
根据各种标准
选择指定实现的功能.如:
cpp
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"),
target_feature = "avx2"))]
fn foo() {
#[cfg(target_arch = "x86")]
use std::arch::x86::_mm256_add_epi64;
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::_mm256_add_epi64;
unsafe {
_mm256_add_epi64(...);
}
}
在此,根据目标机器,使用cfg
标志选择正确的版本;在x86
上,使用该版本,在x86_64
上,使用其版本.也可在运行时
选择:
cpp
fn foo() {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
if is_x86_feature_detected!("avx2") {
return unsafe { foo_avx2() };
}
}
foo_fallback();
}
在此,有两个
版本函数:一个使用可让你执行256
位操作的指定类型的SIMD
功能的AVX2
.
is_x86_feature_detected!
宏生成代码,来检测CPU
是否支持AVX2
,如果支持,则调用foo_avx2
函数.如果没有,则回退到非AVX
实现,foo_fallback
.
std::arch
专门用来构建
这类工作.并在高级上用稳定std::simd
的模块.
如下,没有simd
.
cpp
let lots_of_3s = (&[-123.456f32; 128][..]).iter()
.map(|v| {
9.0 * v.abs().sqrt().sqrt().recip().ceil().sqrt() - 4.0 - 2.0
})
.collect::<Vec<f32>>();
要用SIMD
,请更改为:
cpp
let lots_of_3s = (&[-123.456f32; 128][..]).simd_iter()
.simd_map(f32s(0.0), |v| {
f32s(9.0) * v.abs().sqrt().rsqrt().ceil().sqrt() - f32s(4.0) - f32s(2.0)
})
.scalar_collect();
它几乎是一样的:simd_iter
而不是iter
,simd_map
而不是map
,f32s(2.0)
而不是2.0
.但是,你获得为你生成的SIMD
化版本.
dyn
特征
给定Foo
特征,如下是一个特征对象
:
cpp
Box<Foo>
在Rust1.27
中,稳定了一个新的dyn Trait
语法.特征对象
现在如下:
cpp
//旧`=>`新
Box<Foo> => Box<dyn Foo>
&Foo => &dyn Foo
&mut Foo => &mut dyn Foo
同样,对其他指针类型,Arc<Foo>
现在是Arc<dyn Foo>
等.
开发了可自动升级
代码到更新
习惯用语的rustfix
的工具.
#[must_use]
函数
最后,升级了#[must_use]
属性:现在可用于函数.
以前,它仅适合Result<T,E>
等类型.但是现在,可这样
:
cpp
#[must_use]
fn double(x: i32) -> i32 {
2 * x
}
fn main() {
double(4); //警告:必须使用的`"double"`的未使用返回值
let _ = double(4); //(无警告)
}
稳定了几个新API
:
cpp
DoubleEndedIterator::rfind
DoubleEndedIterator::rfold
DoubleEndedIterator::try_rfold
Duration::from_micros
Duration::from_nanos
Duration::subsec_micros
Duration::subsec_millis
HashMap::remove_entry
Iterator::try_fold
Iterator::try_for_each
NonNull::cast
Option::filter
String::replace_range
Take::set_limit
hint::unreachable_unchecked
os::unix::process::parent_id
process::id
ptr::swap_nonoverlapping
slice::rsplit_mut
slice::rsplit
slice::swap_with_slice
Cargo
更改目标目录
,用--target-dir
标志.
Cargo
试自动发现项目中的测试,示例和二进制文件
.但是,有时需要显式配置.
快速摘要:
1,RLS
不再干扰
命令行生成
2,有时,Rustfmt
停止了错误
文本格式
3,不再允许通过非终止Trait
的impl Trait
返回main
.
4,对implTrait
类型的方法参数
,不再适合::<>(turbofish)
5,NaN>NaN
在常量
环境中,不再返回true
.
6,Rustup
应该不会再因为缺少文档
而失败
7,如果代码继续编译,则只有更改浮点比较
,可能会改变行为.
cpp
fn main() -> Result<(), std::io::Error> {
Ok(())
}
如下.
cpp
struct Foo;
impl Foo {
fn bar(&self, _arg: impl Copy) {}
}
fn main() {
Foo.bar::<u32>(0);
}
浮点比较
:
cpp
use std::f64::NAN;
const FOO: bool = ::std::f64::NAN >= ::std::f64::NAN;
# On 1.26.0
assert_eq!(FOO, true);
# On 1.26.1
assert_eq!(FOO, false);//假