温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

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

发布时间:2021-10-13 10:51:25 来源:亿速云 阅读:370 作者:iii 栏目:编程语言

本篇内容主要讲解“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<T: ?Sized> {
    fn as_ref(&self) -> &T;
}

pub trait AsMut<T: ?Sized> {
    fn as_mut(&mut self) -> &mut T;
}

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

fn iter_bytes<T: AsRef<[u8]>>(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<Borrowed: ?Sized> {
    fn borrow(&self) -> &Borrowed;
}

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

但区别在于两点:

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

impl<T: ?Sized> Borrow<T> for T {
    fn borrow(&self) -> &T {
        self
    }
}

impl<T: ?Sized> Borrow<T> for &T {
    fn borrow(&self) -> &T {
        &**self
    }
}

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

From / Into

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

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

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

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

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

ToOwned

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

pub trait ToOwned {
    type Owned: Borrow<Self>;

    fn to_owned(&self) -> Self::Owned;

    fn clone_into(&self, target: &mut Self::Owned) {
        *target = self.to_owned();
    }
}

impl<T> ToOwned for T
where
    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<T: fmt::Display + ?Sized> 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<Self, Self::Err>;
}

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<Self::Item>;
    ...
}

它最主要的方法是next(),返回一个Option<Item>,迭代完成则返回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<Rhs = Self> {
    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常用的标准库工具有哪些”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI