国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Rust編程語言入門之高級特性

這篇具有很好參考價值的文章主要介紹了Rust編程語言入門之高級特性。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

高級特性

主要內(nèi)容

  • 不安全 Rust
  • 高級 Trait
  • 高級 類型
  • 高級函數(shù)和閉包

一、不安全 Rust

匹配命名變量

  • 隱藏著第二個語言,它沒有強(qiáng)制內(nèi)存安全保證:Unsafe Rust(不安全的 Rust)
    • 和普通的 Rust 一樣,但提供了額外的“超能力”
  • Unsafe Rust 存在的原因:
    • 靜態(tài)分析是保守的。
      • 使用 Unsafe Rust:我知道自己在做什么,并承擔(dān)相應(yīng)風(fēng)險
    • 計(jì)算機(jī)硬件本身就是不安全的,Rust需要能夠進(jìn)行底層系統(tǒng)編程

Unsafe 超能力

  • 使用 unsafe 關(guān)鍵字來切換到 unsafe Rust,開啟一個塊,里面放著 Unsafe 代碼
  • Unsafe Rust 里可執(zhí)行的四個動作(unsafe 超能力):
    • 解引用原始指針
    • 調(diào)用 unsafe 函數(shù)或方法
    • 方法或修改可變的靜態(tài)變量
    • 實(shí)現(xiàn) unsafe trait
  • 注意:
    • Unsafe 并沒有關(guān)閉借用檢查或停用其它安全檢查
    • 任何內(nèi)存安全相關(guān)的錯誤必須留在 unsafe 塊里
    • 盡可能隔離 Unsafe 代碼,最好將其封裝在安全的抽象里,提供安全的API

解引用原始指針

  • 原始指針
    • 可變的:*mut T
    • 不可變的:*const T。意味著指針在解引用后不能直接對其進(jìn)行賦值
    • 注意:這里的 * 不是解引用符號,它是類型名的一部分。
  • 與引用不同,原始指針:
    • 允許通過同時具有不可變和可變指針或多個執(zhí)行同一位置的可變指針來忽略借用規(guī)則
    • 無法保證能指向合理的內(nèi)存
    • 允許為null
    • 不實(shí)現(xiàn)任何自動清理
  • 放棄保證的安全,換取更好的性能/與其它語言或硬件接口的能力

解引用原始指針

fn main() {
  let mut num = 5;
  
  let r1 = &num as *const i32;
  let r2 = &mut num as *mut i32;
  unsafe {
    println!("r1: {}", *r1);
    println!("r2: {}", *r2);
  }
  
  let address = 0x012345usize;
  let r = address as *const i32;
  unsafe {
    println!("r: {}", *r); // 報錯 非法訪問
  }
}
  • 為什么要用原始指針?
    • 與 C 語言進(jìn)行接口
    • 構(gòu)建借用檢查器無法理解的安全抽象

調(diào)用 unsafe 函數(shù)或方法

  • unsafe 函數(shù)或方法:在定義前加上了 unsafe 關(guān)鍵字
    • 調(diào)用前需手動滿足一些條件(主要靠看文檔),因?yàn)镽ust無法對這些條件進(jìn)行驗(yàn)證
    • 需要在 unsafe 塊里進(jìn)行調(diào)用
unsafe fn dangerous() {}

fn main() {
  unsafe {
    dangerous();
  }
}

創(chuàng)建 Unsafe 代碼的安全抽象

  • 函數(shù)包含 unsafe 代碼并不意味著需要將整個函數(shù)標(biāo)記為 unsafe
  • 將 unsafe 代碼包裹在安全函數(shù)中是一個常見的抽象
fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
  let len = slice.len();
  
  assert!(mid <= len);
  
  (&mut slice[..mid], &mut slice[mid..]) // 報錯 cannot borrow `*slice` as mutable more than once at a time
}

fn main() {
  let mut v = vec![1, 2, 3, 4, 5, 6];
  
  let r = &mut v[..];
  let (a, b) = r.split_at_mut(3);
  assert_eq!(a, &mut [1, 2, 3]);
  assert_eq!(b, &mut [4, 5, 6]);
}

修改之后:

use std::slice;

fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
  let len = slice.len();
  let ptr = slice.as_mut_ptr()
  
  assert!(mid <= len);
  
  unsafe {
    (
      slice::from_raw_parts_mut(ptr, mid),
      slice::from_raw_parts_mut(ptr.add(mid), len = mid),
    )
  }
}

fn main() {
  let address = 0x012345usize;
  let r = address as *mut i32;
  
  let slice: &[i32] = unsafe {
    slice::from_raw_parts_mut(r, 10000)
  };
}

使用 extern 函數(shù)調(diào)用外部代碼

  • extern 關(guān)鍵字:簡化創(chuàng)建和使用外部函數(shù)接口(FFI)的過程。
  • 外部函數(shù)接口(FFI,F(xiàn)oreign Function Interface):它允許一種編程語言定義函數(shù),并讓其它編程語言能調(diào)用這些函數(shù)
extern "C" {
  fn abs(input: i32) -> i32;
}

fn main() {
  unsafe {
    println!("Absolute value of -3 according to C: {}", abs(-3));
  }
}
  • 應(yīng)用二進(jìn)制接口(ABI,Application Binary Interface):定義函數(shù)在匯編層的調(diào)用方式
  • “C” ABI 是最常見的ABI,它遵循 C 語言的ABI

從其它語言調(diào)用 Rust 函數(shù)

  • 可以使用 extern 創(chuàng)建接口,其它語言通過它們可以調(diào)用 Rust 的函數(shù)
  • 在 fn 前添加 extern 關(guān)鍵字,并指定 ABI
  • 還需添加 #[no_mangle]注解:避免 Rust 在編譯時改變它的名稱
#[no_mangle]
pub extern "C" fn call_from_c() {
  println!("Just called a Rust function from C!");
}

fn main() {}

訪問或修改一個可變靜態(tài)變量

  • Rust 支持全局變量,但因?yàn)樗袡?quán)機(jī)制可能產(chǎn)生某些問題,例如數(shù)據(jù)競爭
  • 在 Rust 里,全局變量叫做靜態(tài)(static)變量
static HELLO_WORLD: &str = "Hello, world!";

fn main() {
  println!("name is: {}", HELLO_WORLD);
}

靜態(tài)變量

  • 靜態(tài)變量與常量類似
  • 命名:SCREAMING_SNAKE_CASE
  • 必須標(biāo)注類型
  • 靜態(tài)變量只能存儲 'static 生命周期的引用,無需顯示標(biāo)注
  • 訪問不可變靜態(tài)變量是安全的

常量和不可變靜態(tài)變量的區(qū)別

  • 靜態(tài)變量:有固定的內(nèi)存地址,使用它的值總會訪問同樣的數(shù)據(jù)
  • 常量:允許使用它們的時候?qū)?shù)據(jù)進(jìn)行復(fù)制
  • 靜態(tài)變量:可以是可變的,訪問和修改靜態(tài)可變變量是不安全(unsafe)的
static mut COUNTER: u32 = 0;

fn add_to_count(inc: u32) {
  unsafe {
    COUNTER += inc;
  }
}

fn main() {
  add_to_count(3);
  
  unsafe {
    println!("COUNTER: {}", COUNTER);
  }
}

實(shí)現(xiàn)不安全(unsafe)trait

  • 當(dāng)某個 trait 中存在至少一個方法擁有編譯器無法校驗(yàn)的不安全因素時,就稱這個 trait 是不安全的
  • 聲明 unsafe trait:在定義前加 unsafe 關(guān)鍵字
    • 該 trait 只能在 unsafe 代碼塊中實(shí)現(xiàn)
unsafe trait Foo {
  // methods go here
}

unsafe impl Foo for i32 {
  // method implementations go here
}

fn main() {}

何時使用 unsafe 代碼

  • 編譯器無法保證內(nèi)存安全,保證 unsafe 代碼正確并不簡單
  • 有充足理由使用 unsafe 代碼時,就可以這樣做
  • 通過顯示標(biāo)記 unsafe,可以在出現(xiàn)問題時輕松的定位

二、高級 Trait

在 Trait 定義中使用關(guān)聯(lián)類型來指定占位類型

  • 關(guān)聯(lián)類型(associated type)是 Trait中的類型占位符,它可以用于Trait的方法簽名中:
    • 可以定義出包含某些類型的 Trait,而在實(shí)現(xiàn)前無需知道這些類型是什么
pub trait Iterator {
  type Item;
  
  fn next(&mut self) -> Option<Self::Item>;
}

fn main() {
  println!("Hello, world!");
}

關(guān)聯(lián)類型與泛型的區(qū)別

泛型 關(guān)聯(lián)類型
每次實(shí)現(xiàn) Trait 時標(biāo)注類型 無需標(biāo)注類型
可以為一個類型多次實(shí)現(xiàn)某個 Trait(不同的泛型參數(shù)) 無法為單個類型多次實(shí)現(xiàn)某個 Trait

例子:

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

pub trait Iterator2<T> {
  fn next(&mut self) -> Option<T>;
}

struct Counter {}

impl Iterator for Counter {
  type Item = u32;
  
  fn next(&mut self) -> Option<Self::Item> {
    None
  }
}

impl Iterator2<String> for Counter {
  fn next(&mut self) -> Option<String> {
    None
  }
}

impl Iterator2<u32> for Counter {
  fn next(&mut self) -> Option<u32> {
    None
  }
}

fn main() {
  println!("Hello, world!");
}

默認(rèn)泛型參數(shù)和運(yùn)算符重載

  • 可以在使用泛型參數(shù)時為泛型指定一個默認(rèn)的具體類型。
  • 語法:<PlaceholderType=ConcreteType>
  • 這種技術(shù)常用于運(yùn)算符重載(operator overloading)
  • Rust 不允許創(chuàng)建自己的運(yùn)算符及重載任意的運(yùn)算符
  • 但可以通過實(shí)現(xiàn) std::ops 中列出的那些 trait 來重載一部分相應(yīng)的運(yùn)算符

例子一:

use std::ops::Add;

#[derive(Debug, PartialEq)]
struct Point {
  x: i32,
  y: i32,
}

impl Add for Point {
  type Output = Point;
  
  fn add(self, other: Point) -> Point {
    Point {
      x: self.x + other.x,
      y: self.y + other.y,
    }
  }
}

fn main() {
  assert_eq!(Point {x: 1, y: 0} + Point {x: 2, y: 3},
    Point {x: 3, y: 3}
  );
}

例子二:

use std::ops::Add;

struct Millimeters(u32);
struct Meters(u32);

impl Add<Meters> for Millimeters {
  type Output = Millimeters;
  
  fn add(self, other: Meters) -> Millimeters {
    Millimeters(self.0 + (other.0 * 1000))
  }
}

fn main() {
  
}

默認(rèn)泛型參數(shù)的主要應(yīng)用場景

  • 擴(kuò)展一個類型而不破壞現(xiàn)有代碼
  • 允許在大部分用戶都不需要的特定場景下進(jìn)行自定義

完全限定語法(Fully Qualified Syntax)如何調(diào)用同名方法

例子一:

trait Pilot {
  fn fly(&self);
}

trait Wizard {
  fn fly(&self);
}

struct Human;

impl Pilot for Human {
  fn fly(&self) {
    println!("This is your captain speaking.");
  }
}

impl Wizard for Human {
  fn fly(&self) {
    println!("Up!");
  }
}

impl Human {
  fn fly(&self) {
    println!("*waving arms furiously*");
  }
}

fn main() {
  let persion = Human;
  person.fly(); // Human 本身的 fly 方法
  Pilot::fly(&person);
  Wizard::fly(&person);
}

例子二:

trait Animal {
  fn baby_name() -> String;
}

struct Dog;

impl Dog {
  fn baby_name() -> String {
    String::from("Spot")
  }
}

impl Animal for Dog {
  fn baby_name() -> String {
    String::from("puppy")
  }
}

fn main() {
  println!("A baby dog is called a {}", Dog::baby_name()); // Dog 本身的關(guān)聯(lián)方法
}

完全限定語法(Fully Qualified Syntax)如何調(diào)用同名方法

  • 完全限定語法:<Type as Trait>::function(receiver_if_method, netx_arg, ...);
    • 可以在任何調(diào)用函數(shù)或方法的地方使用
    • 允許忽略那些從其它上下文能推導(dǎo)出來的部分
    • 當(dāng) Rust 無法區(qū)分你期望調(diào)用哪個具體實(shí)現(xiàn)的時候,才需使用這種語法
trait Animal {
  fn baby_name() -> String;
}

struct Dog;

impl Dog {
  fn baby_name() -> String {
    String::from("Spot")
  }
}

impl Animal for Dog {
  fn baby_name() -> String {
    String::from("puppy")
  }
}

fn main() {
  println!("A baby dog is called a {}", Dog::baby_name()); // Dog 本身的關(guān)聯(lián)方法
  println!("A baby dog is called a {}", <Dog as Animal>::baby_name());
}

使用 supertrait 來要求 trait 附帶其它 trait 的功能

  • 需要在一個 trait 中使用其它 trait 的功能:
    • 需要被依賴的 trait 也被實(shí)現(xiàn)
    • 那個被間接依賴的 trait 就是當(dāng)前 trait 的 supertrait
use std::fmt;

trait OutlinePrint: fmt::Display {
  fn outline_print(&self) {
    let output = self.to_string();
    let len = output.len();
    println!("{}", "*".repeat(len + 4));
    println!("*{}*", " ".repeat(len + 2));
    println!("* {} *", output);
    println!("*{}*", " ".repeat(len + 2));
    println!("{}", "*".repeat(len + 4));
  }
}

struct Point {
  x: i32,
  y: i32,
}

impl OutlinePrint for Point {}

impl fmt::Display for Point {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "({}, {})", self.x, self.y)
  }
}

fn main() {}

使用 newtype 模式在外部類型上實(shí)現(xiàn)外部 trait

  • 孤兒規(guī)則:只有當(dāng) trait 或類型定義在本地包時,才能為該類型實(shí)現(xiàn)這個 trait
  • 可以通過 newtype 模式來繞過這一規(guī)則
    • 利用 tuple struct (元組結(jié)構(gòu)體)創(chuàng)建一個新的類型
use std::fmt;

struct Wrapper(Vec<String>);

impl fmt::Display for Wrapper {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "[{}]", self.0.join(", "))
  }
}

fn main() {
  let w = Wrapper(vec![String::from("hello"), String::from("world")]);
  println!("w = {}", w);
}

三、高級類型

使用 newtype 模式實(shí)現(xiàn)類型安全和抽象

  • newtype 模式可以:
    • 用來靜態(tài)的保證各種值之間不會混淆并表明值的單位
    • 為類型的某些細(xì)節(jié)提供抽象能力
    • 通過輕量級的封裝來隱藏內(nèi)部實(shí)現(xiàn)細(xì)節(jié)

使用類型別名創(chuàng)建類型同義詞

  • Rust 提供了類型別名的功能:
    • 為現(xiàn)有類型生產(chǎn)另外的名稱(同義詞)
    • 并不是一個獨(dú)立的類型
    • 使用 type 關(guān)鍵字
  • 主要用途:減少代碼字符重復(fù)

例子一:

type Kilometers = i32;

fn main() {
  let x: i32 = 5;
  let y: Killometers = 5;
  println!("x + y = {}", x + y);
}

例子二:

fn takes_long_type(f: Box<dyn Fn() + Send + 'static>) {
  // --snip--
}

fn returns_long_type() -> Box<dyn Fn() + Send + 'static> {
  Box::new(|| println!("hi"))
}

fn main() {
  let f: Box<dyn Fn() + Send + 'static> = Box::new(|| println!("hi"));
}

修改之后:

type Thunk = Box<dyn Fn() + Send + 'static>;

fn takes_long_type(f: Thunk) {
  // --snip--
}

fn returns_long_type() -> Thunk {
  Box::new(|| println!("hi"))
}

fn main() {
  let f: Thunk = Box::new(|| println!("hi"));
}

例子三:

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>;
}

fn main() {
  
}

修改之后:

use std::fmt;

// type Result<T> = Result<T, std::io::Error>; // 聲明在 std::io 中

type Result<T> = std::io::Result<T>;

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<()>;
}

fn main() {
  
}

Never 類型

  • 有一個名為 ! 的特殊類型:
    • 它沒有任何值,行話稱為空類型(empty type)
    • 我們傾向于叫它 never 類型,因?yàn)樗诓环祷氐暮瘮?shù)中充當(dāng)返回類型
  • 不返回值的函數(shù)也被稱作發(fā)散函數(shù)(diverging function)

例子一:

fn bar() -> ! { // 報錯 返回單元類型 不匹配
  
}

fn main() {}

例子二:

fn main() {
  let guess = "";
  
  loop {
    let guess: u32 = match guess.trim().parse() {
      Ok(num) => num,
      Err(_) => continue, // ! never 類型
    };
  }
}

注意:never 類型的表達(dá)式可以被強(qiáng)制的轉(zhuǎn)化為任意其它類型

例子三:

impl<T> Option<T> {
  pub fn unwrap(self) -> T {
    match self {
      Some(val) => val,
      None => panic!("called `Option::unwrap()` on a `None` value"), // !
    }
  }
}

例子四:

fn main() {
  println!("forever");
  
  loop {
    println!("and ever");
  }
}

動態(tài)大小和 Sized Trait

  • Rust 需要在編譯時確定為一個特定類型的值分配多少空間。
  • 動態(tài)大小的類型(Dynamically Sized Types,DST)的概念:
    • 編寫代碼時使用只有在運(yùn)行時才能確定大小的值
  • str 是動態(tài)大小的類型(注意不是 &str):只有運(yùn)行時才能確定字符串的長度
    • 下列代碼無法正常工作:
      • let s1: str = "Hello there!";
      • let s2: str = "How's it going?";
    • 使用 &str 來解決:
      • str 的地址
      • str 的長度

Rust使用動態(tài)大小類型的通用方式

  • 附帶一些額外的元數(shù)據(jù)來存儲動態(tài)信息的大小
    • 使用動態(tài)大小類型時總會把它的值放在某種指針后邊

另外一種動態(tài)大小的類型:trait

  • 每個 trait 都是一個動態(tài)大小的類型,可以通過名稱對其進(jìn)行引用
  • 為了將 trait 用作 trait 對象,必須將它放置在某種指針之后
    • 例如 &dyn Trait 或 Box (Rc) 之后

Sized trait

  • 為了處理動態(tài)大小的類型,Rust 提供了一個 Sized trait 來確定一個類型的大小在編譯時是否已知
    • 編譯時可計(jì)算出大小的類型會自動實(shí)現(xiàn)這一 trait
    • Rust 還會為每一個泛型函數(shù)隱式的添加 Sized 約束
fn generic<T>(t: T) {}

fn generic<T: Sized>(t: T) {} // 上面的generic 會隱式的轉(zhuǎn)化為這種

fn main() {}
  • 默認(rèn)情況下,泛型函數(shù)只能被用于編譯時已經(jīng)知道大小的類型,可以通過特殊語法解除這一限制

?Sized trait 約束

fn generic<T>(t: T) {}

fn generic<T: Sized>(t: T) {} 

fn generic<T: ?Sized>(t: &T) {} // ? 只能用在 sized上

fn main() {}
  • T 可能是也可能不是 Sized
  • 這個語法只能用在 Sized 上面,不能被用于其它 trait

四、高級函數(shù)和閉包

函數(shù)指針

  • 可以將函數(shù)傳遞給其它函數(shù)
  • 函數(shù)在傳遞過程中會被強(qiáng)制轉(zhuǎn)換成 fn 類型
  • fn 類型就是 “函數(shù)指針(function pointer)”
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);
}

函數(shù)指針與閉包的不同

  • fn 是一個類型,不是一個 trait
    • 可以直接指定 fn 為參數(shù)類型,不用聲明一個以 Fn trait 為約束的泛型參數(shù)
  • 函數(shù)指針實(shí)現(xiàn)了全部3種閉包 trait(Fn、FnMut、FnOnce):
    • 總是可以把函數(shù)指針用作參數(shù)傳遞給一個接收閉包的函數(shù)
    • 所以,傾向于搭配閉包 trait 的泛型來編寫函數(shù):可以同時接收閉包和普通函數(shù)
  • 某些情景,只想接收 fn 而不接收閉包:
    • 與外部不支持閉包的代碼交互:C 函數(shù)

例子一

fn main() {
  let list_of_numbers = vec![1, 2, 3];
  let list_of_strings: Vec<String> = list_of_numbers
  .iter().map(|i| i.to_string()).collect();
  
  let list_of_numbers = vec![1, 2, 3];
  let list_of_strings: Vec<String> = list_of_numbers
  .iter().map(ToString::to_string).collect();
}

例子二

fn main() {
  enum Status {
    Value(u32),
    Stop,
  }
  
  let v = Status::Value(3);
  
  let list_of_statuses: Vec<Status> = (0u32..20).map(Status::Value).collect();
}

返回閉包

  • 閉包使用 trait 進(jìn)行表達(dá),無法在函數(shù)中直接返回一個閉包,可以將一個實(shí)現(xiàn)了該 trait 的具體類型作為返回值。
fn returns_closure() -> Fn(i32) -> i32 { // 報錯 沒有一個已知的大小
  |x| x + 1
}

fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
  Box::new(|x| x + 1)
}

fn main() {
  
}

五、宏

宏 macro

  • 宏在Rust里指的是一組相關(guān)特性的集合稱謂:
    • 使用 macro_rules! 構(gòu)建的聲明宏(declarative macro)
    • 3 種過程宏
      • 自定義 #[derive] 宏,用于 struct 或 enum,可以為其指定隨 derive 屬性添加的代碼
      • 類似屬性的宏,在任何條目上添加自定義屬性
      • 類似函數(shù)的宏,看起來像函數(shù)調(diào)用,對其指定為參數(shù)的 token 進(jìn)行操作

函數(shù)與宏的差別

  • 本質(zhì)上,宏是用來編寫可以生成其它代碼的代碼(元編程,metaprogramming)
  • 函數(shù)在定義簽名時,必須聲明參數(shù)的個數(shù)和類型,宏可處理可變的參數(shù)
  • 編譯器會在解釋代碼前展開宏
  • 宏的定義比函數(shù)復(fù)雜得多,難以閱讀、理解、維護(hù)
  • 在某個文件調(diào)用宏時,必須提前定義宏或?qū)⒑暌氘?dāng)前作用域:
  • 函數(shù)可以在任何位置定義并在任何位置使用

macro_rules! 聲明宏(棄用)

  • Rust 中最常見的宏形式:聲明宏
    • 類似 match 的模式匹配
    • 需要使用 marco_rules!
// let v: Vec<u32> = vec![1, 2, 3];

#[macro_export]
macro_rules! vec {
  ($($x:expr),*) => {
    {
      let mut temp_vec = Vec::new();
      $(
        temp_vec.push($x);
      )*
      temp_vec
    }
  };
}

// let mut temp_vec = Vec::new();
// temp_vec.push(1);
// temp_vec.push(2);
// temp_vec.push(3);
// temp_vec

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

  • 這種形式更像函數(shù)(某種形式的過程)一些
    • 接收并操作輸入的 Rust 代碼
    • 生成另外一些 Rust 代碼作為結(jié)果
  • 三種過程宏:
    • 自定義派生
    • 屬性宏
    • 函數(shù)宏
  • 創(chuàng)建過程宏時:
    • 宏定義必須單獨(dú)放在它們自己的包中,并使用特殊的包類型
use proc_macro;

#[some_attribute]
pub fn some_name(input: TokenStream) -> TokenStream {
  
}

自定義 derive 宏

  • 需求:
    • 創(chuàng)建一個 hello_macro 包,定義一個擁有關(guān)聯(lián)函數(shù) hello_macro 的 HelloMacro trait
    • 我們提供一個能自動實(shí)現(xiàn) trait 的過程宏
    • 在它們的類型上標(biāo)注 #[derive(HelloMacro)],進(jìn)而得到 hello_macro 的默認(rèn)實(shí)現(xiàn)
? cd rust

~/rust
? cargo new hello_macro --lib
     Created library `hello_macro` package

~/rust
? cd hello_macro

hello_macro on  master [?] via ?? 1.67.1
? c

hello_macro on  master [?] via ?? 1.67.1
?


hello_macro on  master [?] is ?? 0.1.0 via ?? 1.67.1 
? cd ..         

~/rust 
? cargo new hello_macro_derive --lib
     Created library `hello_macro_derive` package

~/rust 
? cd hello_macro_derive             

hello_macro_derive on  master [?] via ?? 1.67.1 
? c                    

hello_macro_derive on  master [?] via ?? 1.67.1 
? 

hello_macro_derive 代碼:

Cargo.toml

[package]
name = "hello_macro_derive"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
proc-macro = true

[dependencies]
syn = "2.0.13"
quote = "1.0.26"

src/lib.rs

extern crate proc_macro;

use crate::proc_macro::TokenStream;
use quote::quote;
use syn;
// #[derive(HelloMacro)]
#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
    // Construct a representation of Rust code as a syntax tree
    // that we can manipulate
    let ast = syn::parse(input).unwrap();

    // Build the trait implementation
    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()
}

// DeriveInput {
//     // --snip--

//     ident: Ident {
//         ident: "Pancakes",
//         span: #0 bytes(95..103)
//     },
//     data: Struct(
//         DataStruct {
//             struct_token: Struct,
//             fields: Unit,
//             semi_token: Some(
//                 Semi
//             )
//         }
//     )
// }

hello_macro 代碼:

main.rs

use hello_macro::HelloMacro;

struct Pancakes;

impl HelloMacro for Pancakes {
    fn hello_macro() {
        println!("Hello, Macro! My name is Pancakes!");
    }
}

fn main() {
    Pancakes::hello_macro();
}

// use hello_macro::HelloMacro;
// use hello_macro_derive::HelloMacro;

// #[derive(HelloMacro)]
// struct Pancakes;

// fn main() {
//     Pancakes::hello_macro();
// }

lib.rs

pub trait HelloMacro {
    fn hello_macro();
}

編譯

hello_macro_derive on  master [?] is ?? 0.1.0 via ?? 1.67.1 
? cargo build
   Compiling unicode-ident v1.0.8
   Compiling proc-macro2 v1.0.56
   Compiling quote v1.0.26
   Compiling syn v2.0.15
   Compiling hello_macro_derive v0.1.0 (/Users/qiaopengjun/rust/hello_macro_derive)
    Finished dev [unoptimized + debuginfo] target(s) in 1.54s

hello_macro_derive on  master [?] is ?? 0.1.0 via ?? 1.67.1 
? 

hello_macro on  master [?] is ?? 0.1.0 via ?? 1.67.1 
? cargo build                       
   Compiling hello_macro v0.1.0 (/Users/qiaopengjun/rust/hello_macro)
    Finished dev [unoptimized + debuginfo] target(s) in 0.26s

hello_macro on  master [?] is ?? 0.1.0 via ?? 1.67.1 
? 


創(chuàng)建
~/rust
? cargo new pancakes
     Created binary (application) `pancakes` package

~/rust
? cd pancakes

pancakes on  master [?] via ?? 1.67.1
? c

pancakes on  master [?] via ?? 1.67.1
?


main.rs 文件

use hello_macro::HelloMacro;
use hello_macro_derive::HelloMacro;

#[derive(HelloMacro)]
struct Pancakes;

fn main() {
    Pancakes::hello_macro();
}

Cargo.toml 文件

[package]
name = "pancakes"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
hello_macro = {path = "../hello_macro"}
hello_macro_derive = {path = "../hello_macro_derive"}

運(yùn)行文章來源地址http://www.zghlxwxcb.cn/news/detail-424162.html

pancakes on  master [?] is ?? 0.1.0 via ?? 1.67.1 
? cargo run         
   Compiling hello_macro v0.1.0 (/Users/qiaopengjun/rust/hello_macro)
   Compiling pancakes v0.1.0 (/Users/qiaopengjun/rust/pancakes)
    Finished dev [unoptimized + debuginfo] target(s) in 0.10s
     Running `target/debug/pancakes`
Hello, Macro! My name is Pancakes

pancakes on  master [?] is ?? 0.1.0 via ?? 1.67.1 
? 

類似屬性的宏

  • 屬性宏與自定義 derive 宏類似
    • 允許創(chuàng)建新的屬性
    • 但不是為 derive 屬性生成代碼
  • 屬性宏更加靈活:
    • derive 只能用于 struct 和 enum
    • 屬性宏可以用于任意條目,例如函數(shù)
// #[route(GET, "/")]
// fn index() {}

// #[proc_macro_attribute]
// pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {}

類似函數(shù)的宏

  • 函數(shù)宏定義類似于函數(shù)調(diào)用的宏,但比普通函數(shù)更加靈活
  • 函數(shù)宏可以接收 TokenStream 作為參數(shù)
  • 與另外兩種過程宏一樣,在定義中使用 Rust 代碼來操作 TokenStream
// let sql = sql(SELECT * FROM posts WHERE id=1);

// #[proc_macro]
// pub fn sql(input: TokenStream) -> TokenStream {}

到了這里,關(guān)于Rust編程語言入門之高級特性的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Rust編程語言入門之模式匹配

    模式是Rust中的一種特殊語法,用于匹配復(fù)雜和簡單類型的結(jié)構(gòu) 將模式與匹配表達(dá)式和其他構(gòu)造結(jié)合使用,可以更好地控制程序的控制流 模式由以下元素(的一些組合)組成: 字面值 解構(gòu)的數(shù)組、enum、struct 和 tuple 變量 通配符 占位符 想要使用模式,需要將其與某個值進(jìn)行

    2023年04月22日
    瀏覽(24)
  • Rust編程語言入門之無畏并發(fā)

    Concurrent:程序的不同部分之間獨(dú)立的執(zhí)行(并發(fā)) Parallel:程序的不同部分同時運(yùn)行(并行) Rust無畏并發(fā):允許你編寫沒有細(xì)微Bug的代碼,并在不引入新Bug的情況下易于重構(gòu) 注意:本文中的”并發(fā)“泛指 concurrent 和 parallel 在大部分OS里,代碼運(yùn)行在進(jìn)程(process)中,OS同時

    2023年04月19日
    瀏覽(26)
  • Rust編程語言入門之智能指針

    指針:一個變量在內(nèi)存中包含的是一個地址(指向其它數(shù)據(jù)) Rust 中最常見的指針就是”引用“ 引用: 使用 借用它指向的值 沒有其余開銷 最常見的指針類型 智能指針是這樣一些數(shù)據(jù)結(jié)構(gòu): 行為和指針相似 有額外的元數(shù)據(jù)和功能 通過記錄所有者的數(shù)量,使一份數(shù)據(jù)被多個

    2023年04月16日
    瀏覽(26)
  • Rust編程語言入門之cargo、crates.io

    通過 release profile 來自定義構(gòu)建 在https://crates.io/上發(fā)布庫 通過 workspaces 組織大工程 從 https://crates.io/來安裝庫 使用自定義命令擴(kuò)展 cargo release profile: 是預(yù)定義的 可自定義:可使用不同的配置,對代碼編譯擁有更多的控制 每個 profile 的配置都獨(dú)立于其它的 profile cargo 主要的

    2023年04月09日
    瀏覽(28)
  • Rust編程語言入門之最后的項(xiàng)目:多線程 Web 服務(wù)器

    在 socket 上監(jiān)聽 TCP 連接 解析少量的 HTTP 請求 創(chuàng)建一個合適的 HTTP 響應(yīng) 使用線程池改進(jìn)服務(wù)器的吞吐量 優(yōu)雅的停機(jī)和清理 注意:并不是最佳實(shí)踐 創(chuàng)建項(xiàng)目 main.rs 文件 修改一: 修改二: 修改三: 修改四: 修改五: hello.html 文件 404.html 文件 單線程Web服務(wù)器 開啟線程 lib.r

    2023年04月25日
    瀏覽(25)
  • 【Rust 基礎(chǔ)篇】Rust FFI:連接Rust與其他編程語言的橋梁

    Rust是一種以安全性和高效性著稱的系統(tǒng)級編程語言,具有出色的性能和內(nèi)存安全特性。然而,在現(xiàn)實(shí)世界中,我們很少有項(xiàng)目是完全用一種編程語言編寫的。通常,我們需要在項(xiàng)目中使用多種編程語言,特別是在與現(xiàn)有代碼庫或底層系統(tǒng)交互時。為了實(shí)現(xiàn)跨語言的互操作性,

    2024年02月15日
    瀏覽(29)
  • 如何在 macOS 上安裝 Rust 編程語言

    如何在 macOS 上安裝 Rust 編程語言

    安裝Rust編程語言在Mac上是一個相對簡單的過程,但它可能會涉及多個步驟。在本文中,我將詳細(xì)說明如何在Mac上安裝Rust,并提供一些常見問題的解決方法。請注意,由于軟件和工具可能會發(fā)生變化,因此建議首先查看Rust官方網(wǎng)站以獲取最新的安裝說明。 目錄 1.打開終端 2

    2024年02月01日
    瀏覽(30)
  • Go 與 Rust:現(xiàn)代編程語言的深度對比

    Go 與 Rust:現(xiàn)代編程語言的深度對比

    在快速發(fā)展的軟件開發(fā)領(lǐng)域中,選擇合適的編程語言對項(xiàng)目的成功至關(guān)重要。Go 和 Rust 是兩種現(xiàn)代編程語言,它們都各自擁有一系列獨(dú)特的特性和優(yōu)勢。本文旨在深入比較 Go 和 Rust,從不同的角度分析這兩種語言,包括性能、語言特性、生態(tài)系統(tǒng)、適用場景以及社區(qū)支持。

    2024年04月13日
    瀏覽(31)
  • 【跟小嘉學(xué) Rust 編程】十九、高級特性

    【跟小嘉學(xué) Rust 編程】一、Rust 編程基礎(chǔ) 【跟小嘉學(xué) Rust 編程】二、Rust 包管理工具使用 【跟小嘉學(xué) Rust 編程】三、Rust 的基本程序概念 【跟小嘉學(xué) Rust 編程】四、理解 Rust 的所有權(quán)概念 【跟小嘉學(xué) Rust 編程】五、使用結(jié)構(gòu)體關(guān)聯(lián)結(jié)構(gòu)化數(shù)據(jù) 【跟小嘉學(xué) Rust 編程】六、枚舉

    2024年02月10日
    瀏覽(20)
  • 【編程】Rust語言入門第4篇 字符串

    Rust 中的字符是 Unicode 類型,因此每個字符占據(jù) 4 個字節(jié)內(nèi)存空間,但字符串不一樣,字符串是 UTF-8 編碼,也就是字符串中的字符所占的字節(jié)數(shù)是變化的(1 - 4)。 常見的字符串有兩種: str,通常是引用類型, str ,即字符串字面常量,字符串切片。 std::string::String 類型 str 的變

    2024年02月20日
    瀏覽(26)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包