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

Rust語言 - 接口設計的建議之不意外(unsurprising)

這篇具有很好參考價值的文章主要介紹了Rust語言 - 接口設計的建議之不意外(unsurprising)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

Rust - 接口設計建議之不意外(unsurprising)

書:Rust for Rustaceans

Rust接口設計的原則(建議)

  • 四個原則:
    • 不意外(unsurprising)
    • 靈活(flexible)
    • 顯而易見(obvious)
    • 受約束(constrained)
  • Rust API 指南 GitHub:https://github.com/rust-lang/api-guidelines
  • Rust API 指南 中文:https://rust-chinese-translation.github.io/api-guidelines/
  • Rust API 指南:https://rust-lang.github.io/api-guidelines/

不意外(unsurprising)

  • 最少意外原則:
    • 接口應盡可能直觀(可預測,用戶能猜對)
    • 至少應該不讓人感到驚奇
  • 核心思想:
    • 貼近用戶已經(jīng)知道的東西(不必重學概念)
  • 讓接口可預測:
    • 命名
    • 實現(xiàn)常用的 Traits
    • 人體工程學(Ergonomic)Traits
    • 包裝類型(Wrapper Type)

命名實踐

  • 接口的名稱,應符合慣例,便于推斷其功能
    • 例:
      • 方法 iter,大概率應將 &self 作為參數(shù),并應該返回一個迭代器(iterator)
      • 叫做 into_inner 的方法,大概率應將 self 作為參數(shù),并返回某個包裝的類型
      • 叫做 SomethingError 的類型,應實現(xiàn) std::error::Error,并出現(xiàn)在各類 Result 里
  • 將通用/常用的名稱依然用于相同的目的,讓用戶好猜、好理解
  • 推論:同名的事物應該以相同的方式工作
    • 否則,用戶大概率會寫出錯誤的代碼
  • 遵循 as_, to_, into_ 規(guī)范 用以特定類型轉換
名稱前綴 內(nèi)存代價 所有權
as_ 無代價 borrowed -> borrowed
to_ 代價昂貴 borrowed -> borrowed borrowed -> owned (非 Copy 類型) owned -> owned (Copy 類型)
into_ 視情況而定 owned -> owned (非 Copy 類型)

實現(xiàn)常用的 Trait

  • 用戶通常會假設接口中的一切均可“正常工作”,例:
    • 使用 {:?} 打印任何類型
    • 可發(fā)送任何東西到另外的線程
    • 期望每個類型都是 Clone 的
  • 建議積極實現(xiàn)大部分標準 Trait,即使不立即用到
  • 用戶無法為外部類型實現(xiàn)外部的 Trait
    • 即使能包裝你的接口類型,也難以寫出合理實現(xiàn)

Rust 的 trait 系統(tǒng)堅持 孤兒原則 :大致說的是, 每個 impl 塊必須

  1. 要么存在于定義 trait 的 crate 中,
  2. 要么存在于給類型實現(xiàn) trait 的 crate 中。

所以,定義新類型的 crates 應該盡早實現(xiàn)所有合適的、常見的 traits 。

std 中可給類型實現(xiàn)的、最重要的、常見的 traits 有:

  • Copy
  • Clone
  • Eq
  • PartialEq
  • Ord
  • PartialOrd
  • Hash
  • Debug
  • Display
  • Default

給類型實現(xiàn) Default trait 和空的 new 構造函數(shù)是常見和有必要的。
new 是 Rust 中常規(guī)的構造函數(shù),所以不使用參數(shù)來構造基本的類型時, new 對使用者來說就理應存在。
default 方法功能上與 new 方法一致,所以也應當存在。

建議實現(xiàn) Debug Trait

  • 幾乎所有的類型都能、應該實現(xiàn) Debug
    • #[derive(Debug)],通常是最佳實現(xiàn)方式
      • 注意:派生的 Trait 會為任意泛型參數(shù)添加相同的約束(bound)
    • 利用 fmt::Formatter 提供的各種 debug_xxx 輔助方法手動實現(xiàn)
      • debug_struct
      • debug_tuple
      • debug_list
      • debug_set
      • debug_map

例子一

use std::fmt::Debug;

#[derive(Debug)]
struct Pair<T> {
  a: T,
  b: T,
}

fn main() {
  let pair = Pair {a: 5, b: 10};
  println!("Pair: {:?}", pair); // i32 實現(xiàn)了 Debug Trait 故可以打印出來
}

例子二

use std::fmt::Debug;

struct Person {
  name: String,
}

#[derive(Debug)]
struct Pair<T> {
  a: T,
  b: T,
}

fn main() {
  let pair = Pair {
    a: Person { name: "Dave".to_string() },
    b: Person { name: "Nick".to_string() },
  };
  println!("Pair: {:?}", pair);  // 報錯 `Person` doesn't implement `Debug` Person 沒有實現(xiàn) Debug Trait
}

例子三

use std::fmt;

struct Pair<T> {
  a: T,
  b: T,
}

impl<T: fmt::Debug> fmt::Debug for Pair<T> {
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    f.debug_struct("Pair").field("a", &self.a).field("b", &self.b).finish()
  }
}

fn main() {
  let pair = Pair { a: 5, b: 10 };
  println!("Pair: {:?}", pair);
}

建議實現(xiàn) Send 和 Sync(unpin)

  • 不是 Send 的類型無法放在 Mutex 中,也不能在包含線程池的應用程序中傳遞使用

例子四

#[derive(Debug)]
struct MyBox(*mut u8);

unsafe impl Send for MyBox {}

use std::rc::Rc;

fn main() {
  let mb = MyBox(Box::into_raw(Box::new(42)));
  
  let x = Rc::new(42);
  
  std::thread::spawn(move || {
    println!("{:?}", x); // error: `Rc<i32>` cannot be sent between threads safely
  });
  
  //std::thread::spawn(move || {
  //  println!("{:?}", mb); // mb 實現(xiàn)了 Send Trait 
  //});
}
  • 不是 Sync 的類型無法通過 Arc 共享,也無法被放置在靜態(tài)變量中

例子五

use std::cell::RefCell;
use std::sync::Arc;

fn main() {
  let x = Arc::new(RefCell::new(42));
  std::thread::spawn(move || {
    let mut x = x.borrow_mut(); // error: `RefCell<i32>` cannot be shared between threads safely
    *x += 1;
  });
}
  • 如果沒實現(xiàn)上述 Trait,建議在文檔中說明

建議實現(xiàn) Clone 和 Default

例子六

#[derive(Debug, Clone)]
struct Person {
  name: String,
  age: u32,
}

impl Person {
  fn new(name: String, age: u32) -> Person {
    Person { name, age }
  }
}

fn main() {
  let person1 = Person::new("Alice".to_owned(), 25);
  let person2 = person1.clone();
  
  println!("Person 1: {:?}", person1);
  println!("Person 2: {:?}", person2);
}

例子七

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

fn main() {
  let point = Point::default(); // 提供默認的初始值
  
  println!("Point: ({}, {})", point.x, point.y); // Point: (0, 0)
}
  • 如果沒實現(xiàn)上述 Trait,建議在文檔中說明

建議實現(xiàn) PartialEq、PartialOrd、Hash、Eq、Ord

  • PartialEq 特別有用
    • 用戶會希望使用 == 或 assert_eq! 比較你類型的兩個實例

例子八

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

fn main() {
  let point1 = Point { x: 1, y: 2 };
  let point2 = Point { x: 1, y: 2 };
  let point3 = Point { x: 3, y: 4 };
  
  println!("point1 == point2: {}", point1 == point2);
  println!("point1 == point3: {}", point1 == point3);
}
  • PartialOrd 和 Hash 相對更專門化
    • 將類型作為 Map 中的 Key
      • 須實現(xiàn) PartialOrd,以便進行 Key 的比較
    • 使用 std::collection 的集合類型進行去重的類型
      • 須實現(xiàn) Hash,以便進行哈希計算

例子九

use std::collections::BTreeMap;

#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Clone)]
struct Person {
  name: String,
  age: u32,
}

fn main() {
  let mut ages = BTreeMap::new();
  
  let person1 = Person {
    name: "Alice".to_owned(),
    age: 25,
  };
  let person2 = Person {
    name: "Bob".to_owned(),
    age: 30,
  };
  let person3 = Person {
    name: "Charlie".to_owned(),
    age: 20,
  };
  
  ages.insert(person1.clone(), "Alice's age");
  ages.insert(person2.clone(), "Bob's age");
  ages.insert(person3.clone(), "Charlie's age");
  
  for (person, description) in &ages {
    println!("{}: {} - {:?}", person.name, person.age, description);
  }
}

例子十

use std::collections::HashSet;
use std::hash::{Hash, Hasher};

#[derive(Debug, PartialEq, Eq, Clone)]
struct Person {
  name: String,
  age: u32,
}

impl Hash for Person {
  fn hash<H: Hasher>(&self, state: &mut H) {
    self.name.hash(state);
    self.age.hash(state);
  }
}

fn main() {
  let mut persons = HashSet::new();
  
  let person1 = Person {
    name: "Alice".to_owned(),
    age: 25,
  };
  let person2 = Person {
    name: "Bob".to_owned(),
    age: 30,
  };
  let person3 = Person {
    name: "Charlie".to_owned(),
    age: 20,
  };
  
  persons.insert(person1.clone());
  persons.insert(person2.clone());
  persons.insert(person3.clone());
  
  println!("Persons: {:?}", persons);
}
  • Eq 和 Ord 有額外的語義要求(相對 PartialEq 和 PartialOrd)
    • 只應在確信這些語義適用于你的類型時才實現(xiàn)它們

例子十一

// Eq
// 反身性(Reflexivity):對于任何對象 x,x == x 必須為真。
// 對稱性(Symmetry):對于任何對象 x 和 y,如果 x == y 為真,則 y == x 也必須為真。
// 傳遞性(Transitivity):對于任何對象 x、y 和 z,如果 x == y 為真,并且 y == z 為真,則 x == z 也必須為真。

// Ord
// 自反性(Reflexivity):對于任何對象 x,x <= x 和 x >= x 必須為真。
// 反對稱性(Antisymmetry):對于任何對象 x 和 y,如果 x <= y 和 y <= x 都為真,則 x == y 必須為真。
// 傳遞性(Transitivity):對于任何對象 x、y 和 z,如果 x <= y 和 y <= z 都為真,則 x <= z 必須為真。


fn main() {
  
}

建議實現(xiàn) serde 下的 Serialize、Deserialize

  • serde_derive(crate)提供了機制,可以覆蓋單個字段或枚舉變體的序列化
    • 由于 serde 是第三方庫,你可能不希望強制添加對它的依賴
    • 大多數(shù)庫選擇提供一個 serde 的功能(feature),只有當用戶選擇啟用該功能時才添加對 serde 的支持

例子十二:你寫的庫

[dependencies]
serde = { version = "1.0", optional = true}

[features]
serde = ["serde"]

例子十三:別人用的時候

[dependencies]
mylib = { version = "0.1", features = ["serde"] }

為什么沒建議實現(xiàn) Copy

  • 用戶通常不期望類型是 Copy 的
    • 如果想要兩個副本,通常希望調用 clone
  • Copy 改變了移動給定類型值的語義
    • 讓用戶 surprise
  • Copy 類型受到很多限制,一個最初簡單的類型很容易變得不再滿足 Copy 的要求
    • 例如持有了 String 或者其他非 Copy 的類型 ---> 不得不移除 Copy

例子十四

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

fn main() {
  let point1 = Point { x: 10, y: 20 };
  let point2 = point1; // 這里發(fā)生復制,而不是移動
  
  println!("point1: {:?}", point1);
  println!("point2: {:?}", point2);
}

人體工程學 Trait 實現(xiàn)

  • Rust 不會自動為實現(xiàn) Trait 的類型的引用提供對應的實現(xiàn)
    • Bar 實現(xiàn)了 Trait,也不能將 &Bar 傳遞給 fn foo<T: Trait>(t: T)
      • 因為 Trait 可能包含接受 &mut self 或 self 的方法,而這些方法無法在 &Bar 上調用
    • 對于看到 Trait 只有 &self 方法的用戶來說,這會非常令人驚訝
  • 定義新的 Trait 時,通常需要為下列提供相應的全局實現(xiàn)
    • &T where T: Trait
    • &mut T where T: Trait
    • Box<T> where T: Trait
  • Iterator(迭代器):為類型的引用添加 Trait 實現(xiàn)
    • 對于任何可迭代的類型,考慮為 &MyType 和 &mut MyType 實現(xiàn) IntoIterator
      • 在循環(huán)中可直接使用借用實例,符號用戶預期。

包裝類型(Wrapper Types)

  • Rust 沒有傳統(tǒng)意義上的繼承
  • Deref 和 AsRef 提供了類似繼承的東西
    • 你有一個類型為 T 的值,并滿足 T: Deref<Target = U>,可以在 T 類型值上直接調用類型 U 的方法

例子十五

use std::ops::Deref;

struct MyVec(Vec<i32>);

impl Deref for MyVec {
  type Target = Vec<i32>;
  
  fn deref(&self) -> &Self::Target {
    &self.0
  }
}

fn main() {
  let my_vec = MyVec(vec![1, 2, 3, 4, 5]);
  
  println!("Length: {}", my_vec.len());
  println!("First element: {}", my_vec[0]);
}
  • 如果你提供了相對透明的類型(例 Arc)
    • 實現(xiàn) Deref 允許你的包裝類型在使用點運算符時,自動解引用為內(nèi)部類型,從而可以直接調用內(nèi)部類型的方法
    • 如果訪問內(nèi)部類型不需要任何復雜或潛在的低效邏輯,應考慮實現(xiàn) AsRef,這樣用戶可以輕松地將 &WrapperType 作為 &InnerType 使用
    • 對于大多數(shù)包裝類型,還應該在可能的情況下實現(xiàn) From<InnerType>Into<InnerType>,以便用戶可輕松地添加或移除包裝。

例子十六

use std::ops::Deref;

struct Wrapper(String);

impl Deref for Wrapper {
  type Target = String;
  
  fn deref(&self) -> *Self::Target {
    &self.0
  }
}

impl AsRef<str> for Wrapper {
  fn as_ref(&self) -> &str {
    &self.0
  }
}

impl From<String> for Wrapper {
  fn from(s: String) -> Self {
    Wrapper(s)
  }
}

impl From<Wrapper> for String {
  fn from(wrapper: Wrapper) -> Self {
    wrapper.0
  }
}

fn main() {
  let wrapper = Wrapper::from("Hello".to_string());
  
  // 使用 . 運算符調用內(nèi)部字符串類型的方法
  println!("Length: {}", wrapper.len());
  
  // 使用 as_ref 方法將 Wrapper 轉換為 &str 類型
  let inner_ref: &str = wrapper.as_ref();
  println!("Inner: {}", inner_ref);
  
  // 將 Wrapper 轉換為內(nèi)部類型 String
  let inner_string: String = wrapper.into();
  println!("Inner String: {}", inner_string);
}
  • Borrow Trait (與 Deref 和 AsRef 有些類似)
    • 針對更為狹窄的使用情況進行了定制:
      • 允許調用者提供同一類型的多個本質上相同的變體中的任意一個
        • 可叫做:Equivalent
        • 例:對于一個 HashSet<String>,Borrow 允許調用者提供 &str&String。
          • 雖然使用 AsRef 也可以實現(xiàn)類似的效果,但如果沒有 Borrow 的額外要求,這種實現(xiàn)時不安全的,因為 Borrow 要求目標類型實現(xiàn)的 Hash、Eq、和 Ord 必須與實現(xiàn)類型完全相同
      • Borrow 還為 Borrow<T>、&T&mut T 提供了通用實現(xiàn)
        • 這使得在 Trait 約束中使用它來接受給定類型的擁有值或引用值非常方便。
    • Borrow 僅適用于當你的類型本質上與另一個類型等價時
    • 而 Deref 和 AsRef 則適用于更廣泛地實現(xiàn)你的類型可以“充當”的情況

例子十七文章來源地址http://www.zghlxwxcb.cn/news/detail-475801.html

use std::borrow::Borrow;

fn print_length<S>(string: S)
where
	S: Borrow<str>,
{
  println!("Length: {}", string.borrow().len());
}

fn main() {
  let str1: &str = "Hello";
  let string1: String = String::from("World");
  
  print_length(str1);
  print_length(string1);
}

到了這里,關于Rust語言 - 接口設計的建議之不意外(unsurprising)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

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

領支付寶紅包贊助服務器費用

相關文章

  • 鏈式棧接口設計(C語言)

    /** * @file name: 鏈式棧接口設計 * @brief * @author ni456xinmie@163.com * @date 2024/04/24 * @version 1.0 :版本 * @property :類比于順序棧,鏈式棧也有一個棧頂和棧底。根據(jù)鏈式表特性,將第一個插入的值作為棧底,即尾節(jié)點作為棧底。首節(jié)點作為棧頂。 * @note * CopyRight (c) 2023-2024 ni456xinmie@1

    2024年04月25日
    瀏覽(19)
  • Rust 是一種面向系統(tǒng)編程語言 主要被設計用來解決執(zhí)行速度、安全性、并發(fā)性和可靠性等方面的問題 Rust From First Principles: Building a Minimal Rust

    作者:禪與計算機程序設計藝術 Rust 是一種面向系統(tǒng)編程語言,主要被設計用來解決執(zhí)行速度、安全性、并發(fā)性和可靠性等方面的問題。相比于其他語言來說,它擁有以下優(yōu)點: 高性能: Rust 的運行時是單線程的,但是擁有基于垃圾收集(GC)的自動內(nèi)存管理機制,使得在開

    2024年02月07日
    瀏覽(37)
  • 單向循環(huán)鏈表接口設計(C語言)

    /** * @file name: 單向循環(huán)鏈表接口設計 * @brief :設計單向循環(huán)鏈表,實現(xiàn)各種功能函數(shù)并測試 * @author ni456xinmie@163.com * @date 2024/04/23 * @version 1.0 * @property * @note * CopyRight (c) 2023-2024 ni456xinmie@163.com All Right Reseverd */

    2024年04月24日
    瀏覽(11)
  • 一文了解Go語言的I/O接口設計

    I/O 操作在編程中扮演著至關重要的角色。它涉及程序與外部世界之間的數(shù)據(jù)交換,允許程序從外部,如鍵盤、文件、網(wǎng)絡等地方讀取數(shù)據(jù),也能夠將外界輸入的數(shù)據(jù)重新寫入到目標位置中。使得程序能夠與外部環(huán)境進行數(shù)據(jù)交換、與用戶進行交互、實現(xiàn)數(shù)據(jù)持久化和文件操作

    2024年02月11日
    瀏覽(18)
  • 語言接口:探索大模型優(yōu)先架構的新一代 API 設計

    過去的兩三個月里,在開發(fā)基于大語言模型的軟件時,也一直在思考如何設計一個大模型優(yōu)先架構。而隨著越來越多的團隊加入到這場競賽里,我們會發(fā)現(xiàn):基于大語言模型的軟件架構與過去的不同之處,諸如于:我們需要新一代的 API。 我暫時將這一代 API 稱為:語言接口

    2024年02月09日
    瀏覽(27)
  • 接口優(yōu)化的目錄(建議收藏)

    接口優(yōu)化的目錄(建議收藏)

    目錄 前言 ?編輯 批處理 優(yōu)點 缺點 場景 同步轉異步? 優(yōu)點 缺點 場景 空間換時間 優(yōu)點 缺點 場景 預處理 優(yōu)點 缺點 場景 池化技術 優(yōu)點 缺點 場景 串行改并行 優(yōu)點 缺點 場景 索引 優(yōu)點 缺點 場景 避免大事務 優(yōu)點 缺點 場景 深度分頁 優(yōu)點 缺點 數(shù)據(jù)一致性問題的特殊處理

    2023年04月09日
    瀏覽(29)
  • 支付寶代扣接口簽約的各種問題排查(建議收藏)

    之前對接支付寶商家扣款的時候,在 簽約協(xié)議 的部分卡了很久,今天把之前遇到的簽約問題匯總記錄一下~ ? 首先幫大家捋一下簽約的順序,便于直觀理解: ? ? 其次還需要知道的是,支付寶的商家扣款的簽約接口有 兩個 : 一個是 單獨簽約 接口: ? ? 另一個是 支付并簽

    2024年02月06日
    瀏覽(19)
  • 6個步驟輕松實現(xiàn) postman 接口壓力測試(建議收藏)

    6個步驟輕松實現(xiàn) postman 接口壓力測試(建議收藏)

    這里講是postman做接口并發(fā)測試,基礎用法不做贅述 最后: 可以在我的VX公眾號:【自動化測試老司機】免費領取一份216頁軟件測試工程師面試寶典文檔資料。以及相對應的視頻學習教程免費分享!,其中包括了有基礎知識、Linux必備、Shell、互聯(lián)網(wǎng)程序原理、Mysql數(shù)據(jù)庫、抓

    2024年02月04日
    瀏覽(26)
  • 【Rust 基礎篇】Rust Trait 實現(xiàn):靈活的接口抽象

    Rust是一種以安全性和高效性著稱的系統(tǒng)級編程語言,其設計哲學是在不損失性能的前提下,保障代碼的內(nèi)存安全和線程安全。為了實現(xiàn)這一目標,Rust引入了\\\"所有權系統(tǒng)\\\"、\\\"借用檢查器\\\"等特性,有效地避免了常見的內(nèi)存安全問題。然而,在編程中我們常常需要實現(xiàn)多態(tài)和抽象

    2024年02月15日
    瀏覽(26)
  • 一文1400字使用Jmeter進行http接口測試【建議收藏】

    一文1400字使用Jmeter進行http接口測試【建議收藏】

    本文主要針對http接口進行測試,使用Jmeter工具實現(xiàn)。Jmter工具設計之初是用于做性能測試的,它在實現(xiàn)對各種接口的調用方面已經(jīng)做的比較成熟,因此,本次直接使用Jmeter工具來完成對Http接口的測試。 一、開發(fā)接口測試案例的整體方案: 第一步:我們要分析出測試需求,并

    2024年03月14日
    瀏覽(29)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

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

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

二維碼1

領取紅包

二維碼2

領紅包