Rust语言 - 接口设计的建议之不意外(unsurprising)

打印 上一主题 下一主题

主题 998|帖子 998|积分 2994

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
Rust - 接口设计建议之不意外(unsurprising)

书:Rust for Rustaceans
Rust接口设计的原则(建议)

不意外(unsurprising)


  • 最少意外原则:

    • 接口应尽可能直观(可预测,用户能猜对)
    • 至少应该不让人感到惊奇

  • 核心思想:

    • 贴近用户已经知道的东西(不必重学概念)

  • 让接口可预测:

    • 命名
    • 实现常用的 Traits
    • 人体工程学(Ergonomic)Traits
    • 包装类型(Wrapper Type)

命名实践


  • 接口的名称,应符合惯例,便于推断其功能

    • 例:

      • 方法 iter,大概率应将 &self 作为参数,并应该返回一个迭代器(iterator)
      • 叫做 into_inner 的方法,大概率应将 self 作为参数,并返回某个包装的类型
      • 叫做 SomethingError 的类型,应实现 std::error::Error,并出现在各类 Result 里


  • 将通用/常用的名称依然用于相同的目的,让用户好猜、好理解
  • 推论:同名的事物应该以相同的方式工作

    • 否则,用户大概率会写出错误的代码

  • 遵循 as_, to_, into_ 规范 用以特定类型转换
名称前缀内存代价所有权as_无代价borrowed -> borrowedto_代价昂贵borrowed -> borrowed borrowed -> owned (非 Copy 类型) owned -> owned (Copy 类型)into_视情况而定owned -> owned (非 Copy 类型)实现常用的 Trait


  • 用户通常会假设接口中的一切均可“正常工作”,例:

    • 使用 {:?} 打印任何类型
    • 可发送任何东西到另外的线程
    • 期望每个类型都是 Clone 的

  • 建议积极实现大部分标准 Trait,即使不立即用到
  • 用户无法为外部类型实现外部的 Trait

    • 即使能包装你的接口类型,也难以写出合理实现

Rust 的 trait 系统坚持 孤儿原则 :大致说的是, 每个 impl 块必须

  • 要么存在于定义 trait 的 crate 中,
  • 要么存在于给类型实现 trait 的 crate 中。
所以,定义新类型的 crates 应该尽早实现所有合适的、常见的 traits 。
std 中可给类型实现的、最重要的、常见的 traits 有:
给类型实现 Default trait 和空的 new 构造函数是常见和有必要的。
new 是 Rust 中常规的构造函数,所以不使用参数来构造基本的类型时, new 对使用者来说就理应存在。
default 方法功能上与 new 方法一致,所以也应当存在。
建议实现 Debug Trait


  • 几乎所有的类型都能、应该实现 Debug

    • #[derive(Debug)],通常是最佳实现方式

      • 注意:派生的 Trait 会为任意泛型参数添加相同的约束(bound)

    • 利用 fmt::Formatter 提供的各种 debug_xxx 辅助方法手动实现

      • debug_struct
      • debug_tuple
      • debug_list
      • debug_set
      • debug_map


例子一
  1. use std::fmt::Debug;
  2. #[derive(Debug)]
  3. struct Pair<T> {
  4.   a: T,
  5.   b: T,
  6. }
  7. fn main() {
  8.   let pair = Pair {a: 5, b: 10};
  9.   println!("Pair: {:?}", pair); // i32 实现了 Debug Trait 故可以打印出来
  10. }
复制代码
例子二
  1. use std::fmt::Debug;
  2. struct Person {
  3.   name: String,
  4. }
  5. #[derive(Debug)]
  6. struct Pair<T> {
  7.   a: T,
  8.   b: T,
  9. }
  10. fn main() {
  11.   let pair = Pair {
  12.     a: Person { name: "Dave".to_string() },
  13.     b: Person { name: "Nick".to_string() },
  14.   };
  15.   println!("Pair: {:?}", pair);  // 报错 `Person` doesn't implement `Debug` Person 没有实现 Debug Trait
  16. }
复制代码
例子三
[code]use std::fmt;struct Pair {  a: T,  b: T,}impl fmt::Debug for Pair {  fn fmt(&self, f: &mut fmt::Formatter
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

忿忿的泥巴坨

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表