您好,登錄后才能下訂單哦!
本篇內容主要講解“Rust常用的標準庫工具有哪些”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Rust常用的標準庫工具有哪些”吧!
我們一般使用use
語句把其他模塊的內容引入當前模塊中,但對于標準庫中一些非常常用的工具,每次都寫use
語句就過于冗余,因此標準庫提供了一個std::prelude
模塊,在這個模塊中導出一些最常見的工具,然后編譯器為用戶編寫的每一個crate都自動插入一句話:
use std::prelude::*;
如此,一些最常見的工具就可以直接使用,而無需use
了。對這個std::prelude
模塊所包含內容的權衡是需要細致考量的,因為如果包含的內容過多則會引入很多不被使用的工具,這反倒不美了。當下的std::prelude
模塊中包含的內容在std::prelude::v1
中。
除了as
關鍵字可以用來進行基本類型之間的轉換,Rust還提供了很多trait來實現自定義類型之間的轉換。
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
這兩個trait的設計和AsRef
、AsMut
很類似:
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
默認實現了Borrow
、BorrowMut
,以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
里時可能會出現問題。
AsRef
和Borrow
都是從&T
到&U
的轉換,而From/Into
是從T
到U
的轉換:
pub trait From<T>: Sized { fn from(_: T) -> Self; } pub trait Into<T>: Sized { fn into(self) -> T; }
由于From
和Into
是互逆的一組轉換,因此標準庫提供了這樣一個實現:
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
會自動實現。標準庫中還有一組對應的TryFrom
和TryInto
,他們是為了處理類型轉換過程中可能發生轉換錯誤的情況,因此返回值是Result
類型。
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
提供了其他類型轉換為String
類型的能力:
pub trait ToString { fn to_string(&self) -> String; }
標準庫中為所有實現了Display
trait的類型默認實現了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指的是標準輸入輸出和文件輸入輸出。
我們之前介紹過的println!
宏可以方便地隨手輸出一些信息,但如果要對標準輸入輸出作更精細的控制,則需要調用std::io::stdin()
函數和std::io::stdout()
函數來獲取Stdin
和Stdout
結構體的實例。這兩個實例的簡單用法如下:
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中的字符串類型是String
和str
,它們都是用utf-8
進行編碼的。但是,在具體的操作系統上并不是統一使用utf-8
編碼,為了應付這種情況,Rust中設計了OsString
和OsStr
,這兩種類型使用方法和String
和str
類似,并且它們之間也可以互相轉換。
Rust標準庫提供了std::path::PathBuf
和std::path::Path
來處理路徑,PathBuf
對內部數據擁有所有權,而Path
只是借用,事實上,PathBuf
內部存儲了一個OsString
,而Path
則是存儲了Path
。
Rust的文件操作主要通過std::fs::File
來完成,可以實現打開、創建、復制等文件操作。對于文件的讀寫,就要用到std::io
模塊中的一些trait了,例如Read
和Write
。File
實現了這 兩個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: 4
和1: 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常用的標準庫工具有哪些”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。