千家信息网

Rust常用的标准库工具有哪些

发表于:2024-11-17 作者:千家信息网编辑
千家信息网最后更新 2024年11月17日,本篇内容主要讲解"Rust常用的标准库工具有哪些",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Rust常用的标准库工具有哪些"吧!Prelude我们一般使
千家信息网最后更新 2024年11月17日Rust常用的标准库工具有哪些

本篇内容主要讲解"Rust常用的标准库工具有哪些",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Rust常用的标准库工具有哪些"吧!

Prelude

我们一般使用use语句把其他模块的内容引入当前模块中,但对于标准库中一些非常常用的工具,每次都写use语句就过于冗余,因此标准库提供了一个std::prelude模块,在这个模块中导出一些最常见的工具,然后编译器为用户编写的每一个crate都自动插入一句话:

use std::prelude::*;

如此,一些最常见的工具就可以直接使用,而无需use了。对这个std::prelude模块所包含内容的权衡是需要细致考量的,因为如果包含的内容过多则会引入很多不被使用的工具,这反倒不美了。当下的std::prelude模块中包含的内容在std::prelude::v1中。

类型转换

除了as关键字可以用来进行基本类型之间的转换,Rust还提供了很多trait来实现自定义类型之间的转换。

AsRef / AsMut

AsRef的含义是该类型可以通过调用as_ref方法得到另外一个类型的共享引用,同理,AsMut得到的是另外一个类型的可读写引用,它们的定义如下:

pub trait AsRef {    fn as_ref(&self) -> &T;}pub trait AsMut {    fn as_mut(&mut self) -> &mut T;}

AsRef很适合用于泛型代码中,例如,下面的泛型函数接受各种类型,只要可以被转换为&[u8]即可:

fn iter_bytes>(arg: &T) {    for i in arg.as_ref() {        println!("{}", i);    }}fn main() {    let s = String::from("this is a string");    let v = vec![1, 2, 3];    let c = "hello";    iter_bytes(&s);    iter_bytes(&v);    iter_bytes(&c);}
Borrow / BorrowMut

BorrowBorrowMut这两个trait的设计和AsRefAsMut很类似:

pub trait Borrow {    fn borrow(&self) -> &Borrowed;}pub trait BorrowMut: Borrow {    fn borrow_mut(&mut self) -> &mut Borrowed;}

但区别在于两点:

  • 标准库为所有的T&T&mut T默认实现了BorrowBorrowMut,以Borrow为例:

impl Borrow for T {    fn borrow(&self) -> &T {        self    }}impl Borrow for &T {    fn borrow(&self) -> &T {        &**self    }}impl Borrow for &mut T {    fn borrow(&self) -> &T {        &**self    }}
  • Borrow要求返回的类型,必须和原来的类型具备同样的hash值。这是一个约定,如果违反了这个约定,那么把这个类型放到HashMap里时可能会出现问题。

From / Into

AsRefBorrow都是从&T&U的转换,而From/Into是从TU的转换:

pub trait From: Sized {    fn from(_: T) -> Self;}pub trait Into: Sized {    fn into(self) -> T;}

由于FromInto是互逆的一组转换,因此标准库提供了这样一个实现:

impl Into for Twhere    U: From,{    fn into(self) -> U {        U::from(self)    }}

这段代码的含义是:如果存在U: From,则为类型T实现Into。也就是说,我们只需为类型实现From即可,Into会自动实现。标准库中还有一组对应的TryFromTryInto,他们是为了处理类型转换过程中可能发生转换错误的情况,因此返回值是Result类型。

ToOwned

ToOwned提供一种更泛化的Clone的功能,Clone是从&T类型变量创造一个新的T类型变量,而ToOwned是从一个&T类型变量创造一个新的U类型变量,标准库中也提供了ToOwned调用clone方法的默认实现:

pub trait ToOwned {    type Owned: Borrow;    fn to_owned(&self) -> Self::Owned;    fn clone_into(&self, target: &mut Self::Owned) {        *target = self.to_owned();    }}impl ToOwned for Twhere    T: Clone,{    type Owned = T;    fn to_owned(&self) -> T {        self.clone()    }    fn clone_into(&self, target: &mut T) {        target.clone_from(self);    }}
ToString / FromStr

ToString提供了其他类型转换为String类型的能力:

pub trait ToString {    fn to_string(&self) -> String;}

标准库中为所有实现了Displaytrait的类型默认实现了ToString,而Display可以通过derive实现:

impl ToString for T {    default fn to_string(&self) -> String {        use fmt::Write;        let mut buf = String::new();        buf.write_fmt(format_args!("{}", self))            .expect("a Display implementation returned an error unexpectedly");        buf    }}

FromStr提供了从字符串切片向其他类型转换的能力:

pub trait FromStr: Sized {    type Err;    fn from_str(s: &str) -> Result;}

IO

这里的IO指的是标准输入输出和文件输入输出。

标准输入输出

我们之前介绍过的println!宏可以方便地随手输出一些信息,但如果要对标准输入输出作更精细的控制,则需要调用std::io::stdin()函数和std::io::stdout()函数来获取StdinStdout结构体的实例。这两个实例的简单用法如下:

use std::io::{self, Read};fn main() -> io::Result<()> {    let mut buffer = String::new();    let mut stdin = io::stdin();     stdin.read_to_string(&mut buffer)?;    Ok(())}

为了线程安全考虑,每次读取操作都需要上锁,这降低了效率。解决的方法是手动调用lock()方法,但这又增添了使用标准输入输出的复杂度。

事实上,我们受学生时代做各种C语言大作业的影响,导致我们认为标准输入输出是非常重要的功能,可我们细想一下,正儿八经的命令行程序谁会用标准输入来和用户交互呢?一般都是通过两种方式,一则是调用程序的时候指定参数,另一则是通过文件读取用户配置。

文件输入输出

文件输入输出首先要解决路径问题。Rust中的字符串类型是Stringstr,它们都是用utf-8进行编码的。但是,在具体的操作系统上并不是统一使用utf-8编码,为了应付这种情况,Rust中设计了OsStringOsStr,这两种类型使用方法和Stringstr类似,并且它们之间也可以互相转换。

Rust标准库提供了std::path::PathBufstd::path::Path来处理路径,PathBuf对内部数据拥有所有权,而Path只是借用,事实上,PathBuf内部存储了一个OsString,而Path则是存储了Path

Rust的文件操作主要通过std::fs::File来完成,可以实现打开、创建、复制等文件操作。对于文件的读写,就要用到std::io模块中的一些trait了,例如ReadWriteFile实现了这 两个trait,因此拥有read等读取文件的方法。

下面看一个例子来演示说明文件输入输出的方法:

use std::fs::File;use std::io::{BufRead, BufReader, Read};fn test_read_file() -> Result<(), std::io::Error> {    let mut path = std::env::current_dir().unwrap();    path.push("Cargo.toml");    let mut file = File::open(&path)?;    let mut buffer = String::new();    file.read_to_string(&mut buffer)?;    println!("{}", buffer);    Ok(())}fn main() {    match test_read_file() {        Ok(_) => {}        Err(e) => {            println!("{}", e);        }    }}

容器

和C++的STL类似,Rust的标准库也给我们提供了一些比较常用的容器以及相关的迭代器,目前实现了的容器有:

容器描述
Vec可变长数组,连续存储
VecDeque双向队列,适用于从头部和尾部插入删除数据
LinkedList双向链表,非连续存储
HashMap基于Hash算法存储一系列键值对
BTreeMap基于B树存储一系列键值对
HashSet相当于没有值的HashMap
BTreeSet相当于没有值的BTreeMap
BinaryHeap基于二叉堆实现的优先级队列

这里不详细展开讲,以后会对各个容器的用法和实现原理进行深入探究。

迭代器

Rust中的迭代器是指实现了std::iter::Iterator这个trait的类型,其定义如下:

pub trait Iterator {    type Item;    fn next(&mut self) -> Option;    ...}

它最主要的方法是next(),返回一个Option,迭代完成则返回None。实现了Iterator的类型可直接用于for循环。

迭代器拥有一个很重要的特性,就是它是可组合的,这有点类似于Java中的流式编程。Iterator中有很多方法,它们返回的类型也实现了Iterator,这意味着,我们调用这些方法可以从一个迭代器创造出一个新的迭代器,例如:

fn main() {    let v = vec![1, 2, 3, 4, 5, 6, 7, 8];    let mut iter = v        .iter()        .take(5)        .filter(|&x| x % 2 == 0)        .map(|&x| x * x)        .enumerate();    while let Some((i, v)) = iter.next() {        println!("{}: {}", i, v);    }}

这段代码的含义是:从v这个Vec的前五个元素中筛选元素,要求它必须是2的倍数,并把该元素进行平方,因此,最终的输出结果是0: 41: 16

运算符重载

Rust支持自定义类型重载部分运算符,只需该类型实现std::ops模块下相应的trait即可。以Add为例:

pub trait Add {    type Output;    fn add(self, rhs: Rhs) -> Self::Output;}

它具备一个泛型参数RHS和一个关联类型Output。标准库中早已为基本的数字类型实现了这个trait:

macro_rules! add_impl {    ($($t:ty)*) => ($(        #[stable(feature = "rust1", since = "1.0.0")]        impl Add for $t {            type Output = $t;            #[inline]            #[rustc_inherit_overflow_checks]            fn add(self, other: $t) -> $t { self + other }        }        forward_ref_binop! { impl Add, add for $t, $t }    )*)}add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 }

到此,相信大家对"Rust常用的标准库工具有哪些"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

0