Rust-接口设计建议之不意外(unsurprising)书:RustforRustaceansRust接口设计的原则(建议)四个原则:不意外(unsurprising)灵活(flexible)显而易见(obvious)受约束(constrained)RustAPI
书:Rust for Rustaceans
as_
, to_
, into_
规范 用以特定类型转换名称前缀 | 内存代价 | 所有权 |
---|---|---|
as_ |
无代价 | borrowed -> borrowed |
to_ |
代价昂贵 | borrowed -> borrowed borrowed -> owned (非 Copy 类型) owned -> owned (Copy 类型) |
into_ |
视情况而定 | owned -> owned (非 Copy 类型) |
{:?}
打印任何类型Rust 的 trait 系统坚持 孤儿原则 :大致说的是, 每个 impl
块必须
所以,定义新类型的 crates 应该尽早实现所有合适的、常见的 traits 。
std
中可给类型实现的、最重要的、常见的 traits 有:
给类型实现 Default
trait 和空的 new
构造函数是常见和有必要的。
new
是 Rust 中常规的构造函数,所以不使用参数来构造基本的类型时, new
对使用者来说就理应存在。
default
方法功能上与 new
方法一致,所以也应当存在。
#[derive(Debug)]
,通常是最佳实现方式fmt::Formatter
提供的各种 debug_xxx 辅助方法手动实现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 实现了 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 没有实现 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);
}
例子四
#[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 实现了 Send Trait
//});
}
例子五
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;
});
}
例子六
#[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)
}
例子八
#[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);
}
std::collection
的集合类型进行去重的类型例子九
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
// 反身性(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() {
}
serde_derive
(crate)提供了机制,可以覆盖单个字段或枚举变体的序列化
例子十二:你写的库
[dependencies]
serde = { version = "1.0", optional = true}
[features]
serde = ["serde"]
例子十三:别人用的时候
[dependencies]
mylib = { version = "0.1", features = ["serde"] }
例子十四
#[derive(Debug, Copy, Clone)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let point1 = Point { x: 10, y: 20 };
let point2 = point1; // 这里发生复制,而不是移动
println!("point1: {:?}", point1);
println!("point2: {:?}", point2);
}
fn foo<T: Trait>(t: T)
&T where T: Trait
&mut T where T: Trait
Box<T> where T: Trait
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]);
}
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());
// 使用 . 运算符调用内部字符串类型的方法
println!("Length: {}", wrapper.len());
// 使用 as_ref 方法将 Wrapper 转换为 &str 类型
let inner_ref: &str = wrapper.as_ref();
println!("Inner: {}", inner_ref);
// 将 Wrapper 转换为内部类型 String
let inner_string: String = wrapper.into();
println!("Inner String: {}", inner_string);
}
HashSet<String>
,Borrow 允许调用者提供 &str
或 &String
。Borrow<T>
、&T
和 &mut T
提供了通用实现
例子十七
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);
}
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!