日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

10玩rust_有趣的 Rust 类型系统: Trait

發(fā)布時間:2023/11/27 生活经验 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 10玩rust_有趣的 Rust 类型系统: Trait 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

也許你已經(jīng)學習了標準庫提供的 String 類型,這是一個 UTF-8 編碼的可增長字符串。該類型的結構為:

pub struct String {vec: Vec<u8>,
}

UTF-8 的特性決定了你無法索引 String

let s = "hello";println!("The first letter of s is {}", s[0]); // ERROR!!!

雖然你能切片 String,但對非 ascii 字符,切片是非常危險的,你很難確定邊界在哪里。

fn main() {let s = "玩轉 Rust 類型系統(tǒng)".to_string();println!("{:?}", &s[7..11]); // Rustprintln!("{:?}", &s[12..13]); // thread 'main' panicked at 'byte index 13 is not a char boundary; it is inside '類' (bytes 12..15) of `玩轉 Rust 類型系統(tǒng)`', src/main.rs:5:23
}

怎么辦? 你可以自己造一個支持索引、切片安全的字符串類型。Rust 的類型系統(tǒng)允許你這么做。

你可以將 String 內(nèi)部的 Vec<u8> 換成 Vec<char>,再實現(xiàn)一下 From 這個 tarit。該 tarit 用于類型轉換,這里是將 &str 轉換為 MyString:

#[derive(Debug)]
pub struct MyString {vec: Vec<char>
}impl<'a> From<&'a str> for MyString {fn from(string: &'a str) -> Self {Self {vec: string.chars().collect()}}
}

這時候你已經(jīng)可以玩耍你的 MyString 了。

fn main() {let s: MyString = MyString::from("玩轉 Rust 類型系統(tǒng)");println!("{:?}", s); // MyString { vec: ['也', '說', ' ', 'R', 'u', 's', 't', ' ', '類', '型', '系', '統(tǒng)'] }
}

上面的 debug 輸出可能并不是你想要的,你可以自己實現(xiàn) Debug

use std::fmt;pub struct MyString {vec: Vec<char>
}impl fmt::Debug for MyString {fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {for c in &self.vec {f.write_fmt(format_args!("{}", c))?;}Ok(())}
}

這時候再次運行,可能會輸出你想要的結果。你可以定制 Debug 的輸出結果:

for (i, c) in self.vec.iter().enumerate() {f.write_fmt(format_args!("{}:{}, ", i, c))?;
}

上面代碼將會輸出:

0:也, 1:說, 2: , 3:R, 4:u, 5:s, 6:t, 7: , 8:類, 9:型, 10:系, 11:統(tǒng),

得益于 Rust 的宏系統(tǒng),你可以簡寫上面的一部分:

for (i, c) in self.vec.iter().enumerate() {write!(f, "{}:{}, ", i, c)?;
}

write! 會幫你調(diào)用 write_fmtwrite_fmt 通常來自 fmt::Write 和 io::Write。比如說,Stdout 實現(xiàn)了 io::Write,你可以用 write! 將字符“寫”到標準輸出:

fn main() {use std::io::Write;write!(std::io::stdout(), "{:?}n", "玩轉 Rust 類型系統(tǒng)").unwrap(); // "玩轉 Rust 類型系統(tǒng)"
}

知機識變,得心應手

如果你現(xiàn)在 MyString 所在的 crate 的目錄中執(zhí)行 cargo doc --open 查看 MyString 的文檔,會發(fā)現(xiàn)除了你上面實現(xiàn)的 impl Debug for MyStringimpl<'a> From<&'a str> for MyString,在 “Blanket Implementations” 這一節(jié)中編譯器還幫你“一攬子實現(xiàn)”了幾個 Tarit。我們現(xiàn)在感興趣的是:

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

Into 是幫助你進行類型轉換的。你可以改一下上面創(chuàng)建 MyString 的代碼:

fn main() {// let s: MyString = MyString::from("玩轉 Rust 類型系統(tǒng)");let s: MyString = "玩轉 Rust 類型系統(tǒng)".into();println!("{:?}", s); // 玩轉 Rust 類型系統(tǒng)
}

Into 在某些時候非常有用,比如我想實現(xiàn)一個能容納多種類型的容器:

#[derive(Debug)]
pub enum Value {F32(f32),I32(i32),String(String)
}#[derive(Debug)]
pub struct MyColl {vec: Vec<Value>
}impl MyColl {pub fn new() -> Self {Self {vec: Vec::new()}}pub fn push(&mut self, v: impl Into<Value>) {self.vec.push(v.into())}
}impl From<f32> for Value {fn from(f: f32) -> Value {Value::F32(f)}
}impl From<i32> for Value {fn from(i: i32) -> Value {Value::I32(i)}
}impl From<String> for Value {fn from(s: String) -> Value {Value::String(s)}
}

向該集合 push 元素時,達到了類似于動態(tài)語言的效果:

fn main() {let mut coll = MyColl::new();coll.push(123i32);coll.push(456f32);coll.push("hello".to_string());
}

From 類似的還有 FromStr。標準庫為一些類型實現(xiàn)了 FromStr,比如:

impl FromStr for f32
impl FromStr for Ipv4Addr

借助于 FromStr,你可以方便的將字符串轉換為你需要的類型:

use std::str::FromStr;
use std::net::Ipv4Addr;fn main() {let f = f32::from_str("1.23").unwrap();println!("{:?}", f); // 1.23let addr = Ipv4Addr::from_str("127.0.0.1").unwrap();println!("{:?}", addr); // 127.0.0.1
}

就這? 下面這段代碼才是點睛之筆:

impl str {...pub fn parse<F: FromStr>(&self) -> Result<F, F::Err> {FromStr::from_str(self)}...
}

有了 parse,你就可以:

use std::net::Ipv4Addr;fn main() {let f: f32 = "1.23".parse().unwrap();println!("{:?}", f); // 1.23let addr: Ipv4Addr = "127.0.0.1".parse().unwrap();println!("{:?}", addr); // 127.0.0.1
}

需要說明的是,From, Into, FromStrparse 都是在庫的層面實現(xiàn)了,并沒有在編譯層面“打洞”。掌握了這些,你可以舉一反三構建你自己的類型體系。

機由己發(fā),力從人借

我們把目光轉移到 MyString,為 MyString 實現(xiàn) Deref 和 DerefMut 這兩個 tarit:

use std::ops::{Deref, DerefMut};impl Deref for MyString {type Target = Vec<char>;fn deref(&self) -> &Self::Target {&self.vec}
}impl DerefMut for MyString {fn deref_mut(&mut self) -> &mut Self::Target {&mut self.vec}
}

執(zhí)行 cargo doc 后你會發(fā)現(xiàn) Methods from Deref<Target = Vec<char>> 這一節(jié),像是從 Vec “繼承”了一些方法。不過 Rust 可沒有繼承,這也是利用 tarit。

fn main() {let mut s: MyString = "玩轉 Rust 類型系統(tǒng)".into();println!("{:?}", s); // 玩轉 Rust 類型系統(tǒng)println!("{:?}", s[0]); // 玩println!("{:?}", s[9]); // 型println!("{:?}", &s[3..7]); // ['R', 'u', 's', 't']s[0] = '哇';println!("{:?}", s); // 哇轉 Rust 類型系統(tǒng)
}

現(xiàn)在你已經(jīng)可以索引和切片 MyString,如果你對從 Vec “借”來的東西不滿意,可以自己去實現(xiàn)。

他山之石,可以攻玉

我們再為 MyString 實現(xiàn)一下代器(Iterator)。要實現(xiàn) Iterator 非常簡單,

trait Iterator {type Item;fn next(&mut self) -> Option<Self::Item>;
}

只需要實現(xiàn) next 方法即可。這里還需要一個變量去記錄迭代位置。

impl MyString {pub fn iter(&self) -> Iter {Iter {s: &self.vec,pos: 0}}
}use std::iter::{Iterator, IntoIterator};pub struct Iter<'a> {s: &'a Vec<char>,pos: usize
}impl<'a> Iterator for Iter<'a> {type Item = &'a char;fn next(&mut self) -> Option<Self::Item> {self.s.get(self.pos).map(|c| { self.pos += 1; c })}
}

現(xiàn)在你就可以遍歷字符串了:

for c in s.iter() {println!("{:?}", c);
}

如果你想使用 for c in &s 這種形式,還需要為 MyString 實現(xiàn) IntoIterator 這個 trait:

impl<'a> IntoIterator for &'a MyString {type Item = &'a char;type IntoIter = Iter<'a>;fn into_iter(self) -> Self::IntoIter {self.iter()}
}

不過,你也可以“借用” sliceIter。由于 IterMutIntoIter 實現(xiàn)復雜,也可以借來一用。

IterIterMutIntoIter 的命名時約定俗成的。

最終的代碼為:

impl MyString {pub fn iter(&self) -> std::slice::Iter<char> {self.vec.iter()}pub fn iter_mut(&mut self) -> std::slice::IterMut<char> {self.vec.iter_mut()}pub fn into_iter(self) -> std::vec::IntoIter<char> {self.vec.into_iter()}
}use std::iter::{Iterator, IntoIterator};impl<'a> IntoIterator for &'a MyString {type Item = &'a char;type IntoIter = std::slice::Iter<'a, char>;fn into_iter(self) -> Self::IntoIter {self.vec.iter()}
}impl<'a> IntoIterator for &'a mut MyString {type Item = &'a mut char;type IntoIter = std::slice::IterMut<'a, char>;fn into_iter(self) -> Self::IntoIter {self.vec.iter_mut()}
}impl IntoIterator for MyString {type Item = char;type IntoIter = std::vec::IntoIter<char>;fn into_iter(self) -> Self::IntoIter {self.vec.into_iter()}
}

現(xiàn)在你可以用下面三種方式遍歷 MyString:

for c in &s {println!("{:?}", c);
}for c in &mut s {println!("{:?}", c);
}for c in s {println!("{:?}", c);
}

這就完了?并沒有!這一節(jié)的重點并不是教你如何實現(xiàn) Iterator,而是想說 Iterator 下面有眾多方法:

pub trait Iterator {type Item;fn next(&mut self) -> Option<Self::Item>;fn size_hint(&self) -> (usize, Option<usize>) { ... }fn count(self) -> usize { ... }fn last(self) -> Option<Self::Item> { ... }fn advance_by(&mut self, n: usize) -> Result<(), usize> { ... }fn nth(&mut self, n: usize) -> Option<Self::Item> { ... }fn step_by(self, step: usize) -> StepBy<Self> { ... }fn chain<U>(self, other: U) -> Chain<Self, <U as IntoIterator>::IntoIter>whereU: IntoIterator<Item = Self::Item>,{ ... }fn zip<U>(self, other: U) -> Zip<Self, <U as IntoIterator>::IntoIter>?whereU: IntoIterator,{ ... }fn map<B, F>(self, f: F) -> Map<Self, F>?whereF: FnMut(Self::Item) -> B,{ ... }fn for_each<F>(self, f: F)whereF: FnMut(Self::Item),{ ... }...

你只要實現(xiàn) next 方法,就可以免費、無償使用這些方法。與 Iterator 類似的還有 io::Write 和 io::Read 等。

這種思路雖然在其他語言中也經(jīng)常見到,但在 Rust 中的實現(xiàn)是非常清晰明了的。

trait Log {fn print(&self, s: &str);fn info(&self, s: &str) {self.print(&format!("INFO: {}", s))}fn debug(&self, s: &str) {self.print(&format!("DEBUG: {}", s))}fn error(&self, s: &str) {self.print(&format!("ERROR: {}", s))}
}struct MyPrint;impl Log for MyPrint {fn print(&self, s: &str) {println!("{}", s);}
}fn main() {let print = MyPrint;print.info("hello"); // INFO: helloprint.debug("hello"); // DEBUG: helloprint.error("hello"); // ERROR: hello
}

上面的代碼演示了如何實現(xiàn)一個簡易的日志組件。

本文到這里就結束了。我并不想把 trait 的功能一一列舉一遍,而是以實現(xiàn) MyString 為導向,選取了幾個比較有趣的例子。

總結

以上是生活随笔為你收集整理的10玩rust_有趣的 Rust 类型系统: Trait的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。