27. 高級特性(下)

目錄

  • 一、為了類型安全和抽象而使用 newtype 模式
  • 二、使用類型別名創建類型同義詞
    • 2.1 使用type關鍵賦予現有類型一個別名
    • 2.2 減少重復
    • 2.3 與Result<T, E>結合使用
    • 2.4 從不返回的 never type
  • 三、高級函數和閉包
    • 3.1 函數指針
    • 3.2 返回閉包
  • 四、宏
    • 4.1 宏和函數的區別
    • 4.2 macro_rules! 的聲明宏
    • 4.3 基于屬性生成代碼的過程宏
    • 4.4 編寫自定義 derive 宏

一、為了類型安全和抽象而使用 newtype 模式

  • newtype模式的應用可以用于確保靜態值不被混淆以及表示一個值的單元;
  • newtype模式的應用可以抽象掉一些類型的實現細節;
    • 例如封裝類型可以暴露出與直接使用其內部私有類型時所不同的公有 API,以便限制其功能;
  • newtype模式也可以隱藏其內部的泛型類型;

二、使用類型別名創建類型同義詞

2.1 使用type關鍵賦予現有類型一個別名

fn main() {type Kilometers = i32;let x: i32 = 5;let y: Kilometers = 5;println!("x + y = {}", x + y); 
}
  • 代碼輸出:x + y = 10

2.2 減少重復

  • 類型別名的主要用途是減少重復,例如類型Box<dyn Fn() + Send + 'static>
let f: Box<dyn Fn() + Send + 'static> = Box::new(|| println!("hi"));fn takes_long_type(f: Box<dyn Fn() + Send + 'static>) {}fn returns_long_type() -> Box<dyn Fn() + Send + 'static> {}
  • 通過type關鍵字引入類型別名,則可以修改為
type Thunk = Box<dyn Fn() + Send + 'static>;let f: Thunk = Box::new(|| println!("hi"));fn takes_long_type(f: Thunk) {}fn returns_long_type() -> Thunk {}

2.3 與Result<T, E>結合使用

  • 標準庫中的std::io模塊;
  • I/O 操作通常會返回一個Result<T, E>
  • 標準庫中的std::io::Error結構體代表了所有可能的 I/O 錯誤;
  • std::io中大部分函數會返回Result<T, E>
use std::io::Error;
use std::fmt;pub trait Write {fn write(&mut self, buf: &[u8]) -> Result<usize, Error>;fn flush(&mut self) -> Result<(), Error>;fn write_all(&mut self, buf: &[u8]) -> Result<(), Error>;fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<(), Error>;
}
  • 上述代碼出現了很多Result<..., Error>,因此,std::io有別名聲明;
type Result<T> = std::result::Result<T, std::io::Error>;
  • 由于位于std::io,可用的完全限定的別名是std::io::Result<T>,即Result<T, E> 中 E 放入了 std::io::Error
  • 最后的效果如下
pub trait Write {fn write(&mut self, buf: &[u8]) -> Result<usize>;fn flush(&mut self) -> Result<()>;fn write_all(&mut self, buf: &[u8]) -> Result<()>;fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<()>;
}

2.4 從不返回的 never type

  • Rust有一個! 的特殊類型,它被稱為empty type,更傾向于稱之為never type
  • 主要用于在函數從不返回的時候充當返回值;
  • 從不返回的函數被稱為發散函數
fn bar() -> ! {}

用途

  • 有如下代碼
let guess: u32 = match guess.trim().parse() {Ok(num) => num,Err(_) => continue,
};
  • 我們知道match分支必須返回相同的類型;
  • 如下代碼必無法通過編譯
let guess = match guess.trim().parse() {Ok(_) => 5,Err(_) => "hello",
}
  • 上述代碼里的guess必須既是整型也是字符串,而 Rust 要求guess 只能是一個類型;
  • 所以continue 返回的值是!

never type 的另一個用途是 panic!

三、高級函數和閉包

3.1 函數指針

  • 可以向函數傳遞閉包,也可以向函數傳遞常規函數;
  • 函數的類型是fn,它被稱為函數指針
fn add_one(x: i32) -> i32 {x + 1
}fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {f(arg) + f(arg)
}fn main() {let answer = do_twice(add_one, 5);println!("The answer is: {}", answer);  //The answer is: 12
}
  • do_twice函數中的f被指定為一個接受一個i32 參數并返回 i32 的函數指針;
  • 就可以在do_twice函數體中調用該函數;
  • fn是一個類型而不是一個trait;
    • 直接指定 fn 作為參數;
    • 聲明一個帶有 Fn 作為 trait bound 的泛型參數;
  • 函數指針實現了所有三個閉包 trait:Fn、FnMut 和 FnOnce;
  • 總是可以在調用期望閉包的函數時傳遞函數指針作為參數;
  • 當與不存在閉包的外部代碼交互時,可以只期望接受 fn 而不接受閉包;
let list_of_numbers = vec![1, 2, 3];
let list_of_strings: Vec<String> = list_of_numbers.iter().map(|i| i.to_string()).collect();
  • 上述代碼使用map函數將一個數字vector 轉換為一個字符串 vector;
  • 也可以將函數作為 map 的參數來代替閉包;
let list_of_numbers = vec![1, 2, 3];
let list_of_strings: Vec<String> = list_of_numbers.iter().map(ToString::to_string).collect();
  • 另一個實用的模式暴露了元組結構體和元組結構體枚舉成員的實現細節;
    • 這些項使用 () 作為初始化語法(看起來就像函數調用);
    • 同時它們確實被實現為返回由參數構造的實例的函數;
    • 它們也被稱為實現了閉包 trait 的函數指針,并可以采用類似如下的方式調用;
enum Status {Value(u32),Stop,
}let list_of_statuses: Vec<Status> =(0u32..20).map(Status::Value).collect();
  • 創建了Status::Value實例,它通過map用范圍的每一個u32 值調用 Status::Value 的初始化函數;

3.2 返回閉包

  • 如下的代碼不能通過編譯
fn returns_closure() -> Fn(i32) -> i32 {|x| x + 1
}
  • 使用 trait 對象解決
fn returns_closure() -> Box<dyn Fn(i32) -> i32> {Box::new(|x| x + 1)
}

四、宏

  • 宏(Macro)指的是 Rust 中一組相關特性的集合:
    • 使用macro_rules! 聲明的(Declarative)宏,和三種過程(Procedural)宏:
      1. 自定義#[derive] 宏,用于結構體和枚舉上指定通過derive屬性添加的代碼;
      2. 類似屬性宏,可用于任意項的自定義屬性;
      3. 類函數宏,看起來像函數調用,作用于作為參數傳遞的 token;

4.1 宏和函數的區別

  • 宏是一種為寫其他代碼而編寫的代碼,即所謂的元編程(metaprogramming)
  • 一個函數標簽必須聲明函數參數個數和類型,宏能夠接受不同數量的參數;
  • 在一個文件里調用宏之前必須定義,或將其引入作用域,函數則可以在任何地方定義和調用;

4.2 macro_rules! 的聲明宏

  • 最常用的宏形式是 聲明宏(declarative macros),它允許我們編寫一些類似 Rust match 表達式的代碼 ;
  • 使用macro_rules!定義宏;
  • vec![1, 2, 3];調用下面的宏 (簡化);
#[macro_export]
macro_rules! vec {( $( $x:expr ),* ) => {{let mut temp_vec = Vec::new();$(temp_vec.push($x);)*temp_vec}};
}
  • #[macro_export]標注說明,只要將定義了宏的crate引入作用域,宏就應當是可用的;
  • 沒有該標注的宏不能被引入作用域;
  • 使用macro_rules!和宏名稱開始宏定義,且所定義的宏并不帶感嘆號,名字后跟大括號表示宏定義體;
  • $x:expr指匹配任何的Rust表達式并命名為 $x ,后面的逗號表示傳入的逗號分隔符,后面的*表示能匹配0個或多個;
  • 全部宏語法,參參閱:https://rustwiki.org/zh-CN/reference/macros.html

4.3 基于屬性生成代碼的過程宏

  • 過程宏(procedural macros),更像函數(一種過程類型);
  • 過程宏接收Rust代碼作為輸入,然后產生另一些代碼作為輸出;
  • 還有一種類型的過程宏:
    • 自定義派生;
    • 屬性宏;
    • 函數宏;
  • 創建過程宏時
    • 宏定義必須單獨放在它們自己的包中,并使用特殊的包類型;
use proc_macro;#[some_attribute]
pub fn some_name(input: TokenStream) -> TokenStream {
}
  • some_attribute是過程宏的占位符;
  • 定義過程宏的函數以一個TokenStream作為輸入并產生一個TokenStream作為輸出;
  • TokenStream類型由包含在 Rust 中的proc_macro crate定義,并表示令牌序列;

4.4 編寫自定義 derive 宏

  • 創建hello_macro crate,定義一個擁有關聯函數HelloMacro的 trait 和關聯函數hello_macro
  • 提供一個能自動實現trait的過程宏;
  • 使用戶在它們的類型上標注#[derive(HelloMacro)],進而得到hello_macro的默認實現;

實現

  1. 在一個全新目錄下(稱為工作空間)創建Cargo.toml文件,寫上[workspace]就行了;
  2. 再相同的目錄下輸入下面兩條指令,創建兩個crate;
cargo new hello_macro --lib
cargo new hello_macro_derive --lib
cargo new pancakes
  1. hello_macro_derive\Cargo.toml文件的內容如下
[package]
name = "hello_macro_derive"
version = "0.1.0"
edition = "2021"[lib]
proc-macro = true[dependencies]
syn = "2.0.68"
quote = "1.0"
  1. Cargo.toml文件內容如下
[workspace]members = ["hello_macro", "hello_macro_derive", "pancakes"]
  1. pancakes/Cargo.toml文件內容如下
[package]
name = "pancakes"
version = "0.1.0"
edition = "2021"[dependencies]
hello_macro = {path = "../hello_macro"}
hello_macro_derive = {path = "../hello_macro_derive"}
  1. 將過程宏放到hello_macro_derive里,其lib.rs內容如下
extern crate proc_macro;use crate::proc_macro::TokenStream;
use quote::quote;
use syn;#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {// 將 Rust 代碼解析為語法樹以便進行操作let ast = syn::parse(input).unwrap();// 構建 trait 實現impl_hello_macro(&ast)
}fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {let name = &ast.ident;let gen = quote! {impl HelloMacro for #name {fn hello_macro() {println!("Hello, Macro! My name is {}", stringify!(#name));}}};gen.into()
}
  • proc_macro包提供了編譯器接口,從而可以讀取可操作的Rust代碼;
  • syn是把Rust代碼從字符串轉換為可供我們進一步操作的數據結構;
  • quote包能夠將syn產生的數據結構重新轉換為Rust代碼;
  • 函數hello_macro_derive負責解析TokenStream,函數內部的impl_hello_macro負責轉換語法庫;
  • 效果是:用戶標注#[derive(HelloMacro)]后,hello_macro_derive 會被自動調用;
  • 詳細的看相關的文檔;
  1. hello_macro/src/lib.rs中的代碼為
pub trait HelloMacro{fn hello_macro();
}
  1. pancakes/src/main.rs的代碼為
use hello_macro::HelloMacro;
use hello_macro_derive::HelloMacro;#[derive(HelloMacro)]
struct Pancakes;fn main() {Pancakes::hello_macro();
}
  • 運行的結果如下

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/36002.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/36002.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/36002.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python基礎語法 003-3 數據類型元組

1 元組 1.1 元組含義 1.1.1 元組的表示 #元組的表示方法:() names ("xiaoyun", "xiaoming") print(names)--結果------- (xiaoyun, xiaoming) 1.1.2 空元組 #空元組 names () print(type(names)) print(len(names))----------------結果--------- &l…

安裝vue開發者工具

瀏覽器控制臺提示&#xff1a; 打開網址 GitHub - vuejs/devtools: ?? Browser devtools extension for debugging Vue.js applications. 點擊添加 上圖地址&#xff1a;Installation | Vue Devtools 安裝好了

Spring Boot中如何使用Flyway進行數據庫版本控制

Spring Boot中如何使用Flyway進行數據庫版本控制 大家好&#xff0c;我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01;在現代的軟件開發中&#xff0c;數據庫版本控制是保證應用程序…

群體優化算法---石墨烯優化算法介紹以及在期權定價上的應用(Black-Scholes模型來計算歐式期權的理論價格)

介紹 石墨烯算法是一種新興的優化算法&#xff0c;靈感來自于石墨烯的結構和特性。石墨烯是一種由碳原子構成的二維蜂窩狀晶格結構&#xff0c;具有優異的機械、電學和熱學性能。石墨烯算法通過模擬石墨烯原子之間的相互作用和遷移&#xff0c;來求解復雜的優化問題 基本概念…

K8S -理解StatefulSet - 部署有狀態應用

什么是 有狀態服務和 無狀態服務 有狀態服務&#xff08;Stateful Service&#xff09;&#xff1a; 有狀態服務是指在處理請求期間維護和跟蹤用戶狀態或會話信息的服務。這意味著服務在多個請求之間保持狀態&#xff0c;并且需要在請求之間共享和使用這些狀態信息。通常&…

Websocket在Java中的實踐——握手攔截器

在《Websocket在Java中的實踐——最小可行案例》一文中&#xff0c;我們看到如何用最簡單的方式實現Websocket通信。本文中&#xff0c;我們將介紹如何在握手前后進行干涉&#xff0c;以定制一些特殊需求。 在《Websocket在Java中的實踐——最小可行案例》的基礎上&#xff0c;…

PID原理及控制算法詳解

文章目錄 1. 概念 1.1 PID框圖 1.2 具體示例&#xff1a;無人機高度控制 2. PID原理 3. 常用術語 4. 計算過程 4.1 比例控制&#xff08;Proportional&#xff09; 4.2 積分控制&#xff08;Integral&#xff09; 4.3 微分控制&#xff08;Derivative&#xff09; 5.…

windows@文件高級共享設置@網絡發現功能@從資源管理器網絡中訪問遠程桌面

文章目錄 高級共享設置常用選項其他選項操作界面說明 網絡類型檢查和設置(專用網絡和公用網絡)&#x1f47a;Note 高級共享設置和防火墻&#x1f47a;命令行方式使用圖形界面方式配置 網絡發現網絡發現功能的詳細介紹網絡發現的作用&#x1f47a;網絡發現的工作原理啟用和配置網…

SOC和SOH聯合估計 | 基于集成ELM的鋰離子電池充電截止電壓下的SOC和SOH聯合估計

目錄 研究概述研究內容研究路線研究介紹研究概述 充電截止電壓是大多數電動汽車用戶充電都會經歷的電壓點。針對傳統安時積分法忽略初始容量誤差和電池老化等一系列待優化的問題,提出了雙層集成極限學習機(extreme learning machine, ELM)算法,實現鋰離子電池充電截止電壓下…

【Python實戰因果推斷】2_因果效應異質性2

目錄 CATE with Regression Evaluating CATE Predictions CATE with Regression 我想你可能已經預料到了&#xff1a;與應用因果推理中的大多數情況一樣&#xff0c;答案往往從線性回歸開始。但在走這條路之前&#xff0c;讓我們把事情變得更具體一些。假設你在一家遍布全國的…

[A133]uboot啟動流程

[A133]uboot啟動流程 hongxi.zhu 2024-6-21 1. 第一階段 lds描述 從u-boot.lds中能找到程序的匯編入口ENTRY(_start) brandy/brandy-2.0/u-boot-2018/u-boot.lds OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUT…

Spring Boot中的異步編程技巧

Spring Boot中的異步編程技巧 大家好&#xff0c;我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01;今天我們將探討在Spring Boot應用程序中如何使用異步編程技巧&#xff0c;以提升性…

LeetCode題練習與總結:單詞拆分--139

一、題目描述 給你一個字符串 s 和一個字符串列表 wordDict 作為字典。如果可以利用字典中出現的一個或多個單詞拼接出 s 則返回 true。 注意&#xff1a;不要求字典中出現的單詞全部都使用&#xff0c;并且字典中的單詞可以重復使用。 示例 1&#xff1a; 輸入: s "l…

vant組件 頂部下拉刷新和頁面底部下拉獲取數據+頂部搜索框

1.html部分&#xff08;頂部tab切換無&#xff0c;只有主體list部分&#xff09; <div class"yd" ><!-- yd端 --><!-- 搜索框 --><van-searchv-model"ydsearchvalue"show-actionplaceholder"請輸入搜索關鍵詞"search"…

JavaEE之HTTP協議(1)_HTTP基礎知識,HTTP 請求、響應格式,方法,狀態碼

一、HTTP協議 1.1 基本概念: HTTP全稱超文本傳輸協議&#xff0c;是一種無狀態的、應用層的協議&#xff0c;它基于請求/響應模型。客戶端&#xff08;通常是Web瀏覽器&#xff09;通過發送HTTP請求到服務器來獲取或發送信息&#xff0c;服務器則返回HTTP響應作為回應。HTTP協…

shell (三)shell腳本

SHELL腳本 編程語言的分類 解釋型語言&#xff1a;shell&#xff0c;Python&#xff0c;需要解析器 編譯型語言&#xff1a;C語言&#xff0c;C&#xff0c;需要編譯器 shell腳本 操作系統的結構 shell&#xff08;貝殼&#xff09; 應用層 app&#xff0c;代碼 應用層需要通…

2024年軟件測試面試題大全【答案+文檔】

&#x1f345; 視頻學習&#xff1a;文末有免費的配套視頻可觀看 &#x1f345; 點擊文末小卡片&#xff0c;免費獲取軟件測試全套資料&#xff0c;資料在手&#xff0c;漲薪更快 一、面試基礎題 簡述測試流程&#xff1a; 1、閱讀相關技術文檔&#xff08;如產品PRD、UI設計…

1、線性回歸模型

1、主要解決問題類型 1.1 預測分析(Prediction) 線性回歸可以用來預測一個變量(通常稱為因變量或響應變量)的值,基于一個或多個輸入變量(自變量或預測變量)。例如,根據房屋的面積、位置等因素預測房價。 1.2 異常檢測(Outlier Detection) 線性回歸可以幫助識別數…

鴻蒙開發系統基礎能力:【@ohos.systemTime (設置系統時間)】

設置系統時間 本模塊用來設置、獲取當前系統時間&#xff0c;設置、獲取當前系統日期和設置、獲取當前系統時區。 說明&#xff1a; 本模塊首批接口從API version 7開始支持。后續版本的新增接口&#xff0c;采用上角標單獨標記接口的起始版本。 導入模塊 import systemTime …

沙盒在數據防泄密領域意義

在信息化快速發展的今天&#xff0c;數據已成為企業最寶貴的資產之一。然而&#xff0c;數據泄密事件頻發&#xff0c;給企業的安全和發展帶來了巨大威脅。SDC沙盒防泄密系統&#xff0c;作為一種創新的數據防泄密解決方案&#xff0c;正逐漸在數據防泄密領域發揮著越來越重要的…