2311rust到27版本更新

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-units1(或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,RefCellUnsafeCell的新功能

2,各种原子整数类型的新函数

3,{integer}::min_valuemax_value

4,MEMsize_ofalign_of

5,ptr::nullnull_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/longjmpRustpanic都使用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_secsfrom_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> {
    //...
}

假设有个针对i32f32实现的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开始,没有&sref初始代码,完全按你的期望编译执行.总之,在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中,现在可声明返回Resultmain:

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::openio::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,不再允许通过非终止Traitimpl 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);//假
相关推荐
老猿讲编程5 小时前
用示例来看C2Rust工具的使用和功能介绍
rust
金庆5 小时前
How to set_default() using config-rs crate
rust·config·set_default·valuekind
许野平7 小时前
Rust: 利用 chrono 库实现日期和字符串互相转换
开发语言·后端·rust·字符串·转换·日期·chrono
‍。。。18 小时前
使用Rust实现http/https正向代理
http·https·rust
Source.Liu18 小时前
【用Rust写CAD】第二章 第四节 函数
开发语言·rust
monkey_meng18 小时前
【Rust中的迭代器】
开发语言·后端·rust
余衫马18 小时前
Rust-Trait 特征编程
开发语言·后端·rust
monkey_meng18 小时前
【Rust中多线程同步机制】
开发语言·redis·后端·rust
hikktn1 天前
如何在 Rust 中实现内存安全:与 C/C++ 的对比分析
c语言·安全·rust
睡觉谁叫~~~1 天前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust