#[derive(Debug, PartialEq, Clone)]
是个啥?
示🌰代码:
#[derive(Debug, PartialEq, Clone)]
pub enum Enum_Ex {
String(String),
Number(f64),
}
🗝️首先,#[derive(Debug, PartialEq, Clone)]
是一个属性。
👉属性(attributes)是用于向编译器提供关于代码段的元数据的特殊语法结构。属性可以应用于模块、crate 或其他项(如函数、结构体、枚举等),用于控制编译器的行为或包含其他类型的元数据。
属性的基本语法
属性的基本语法是使用方括号
[]
,并以#
开头。根据属性的应用范围,属性可以分为外部属性(outer attribute)和内部属性(inner attribute):外部属性 (
#[attribute]
):应用于紧随其后的项。内部属性 (
#![attribute]
):应用于包含它的项(通常是模块或整个 crate)。
在 Rust 中,#[derive]
属性允许自动为某些特定的 trait 生成实现代码。当你在枚举或结构体上使用 #[derive]
属性时,编译器会为你提供这些 trait 的基本实现。这里的 Debug
、PartialEq
和 Clone
是三个可以通过 #[derive]
自动实现的 trait。
Debug
Debug
trait 用于格式化输出,主要用于调试目的。当你为一个类型派生 Debug
trait 时,你可以使用 {:?}
格式化占位符来打印该类型的实例,这对于快速查看实例的状态非常有用。
PartialEq
PartialEq
trait 用于比较两个类型实例的相等性。派生 PartialEq
会自动实现 eq
方法,这使得你可以使用 ==
和 !=
运算符来比较两个实例。对于结构体,如果所有字段都相等,则两个实例相等;对于枚举,相同的变体被认为是相等的。
Clone
Clone
trait 允许你创建类型实例的深拷贝。派生 Clone
会自动实现 clone
方法,这意味着你可以创建一个实例的完整副本。这在需要复制数据而不是移动所有权时非常有用。
这些 trait 的自动派生实现通常足以满足基本需求,但如果你需要更复杂的行为,你也可以选择手动实现这些 trait。例如,如果你的类型包含非标准方式比较相等性的字段,你可能需要为 PartialEq
提供自定义实现。
什么时候不可以使用#[derive(Debug, PartialEq, Clone)]
❓或者说什么情况下我可以使用 #[derive()]
来为结构体自动实现 trait❓
1. 当类型的字段不支持相应的 trait 时
-
Debug
:如果结构体中的某个字段类型没有实现Debug
trait,那么这个结构体就不能自动派生Debug
。例如,如果你有一个字段是某种复杂的闭包或某些自定义类型,而这些类型没有实现Debug
,那么你不能为包含这些字段的结构体派生Debug
。 -
PartialEq
:如果结构体中的任何字段没有实现PartialEq
,那么这个结构体也不能派生PartialEq
。这通常出现在字段类型为某些特殊函数指针或其他复杂类型时。 -
Clone
:如果结构体的任何字段不支持Clone
,那么整个结构体也不能自动派生Clone
。例如,如果字段是某些特殊的资源句柄或包含原始指针,这些类型通常不支持自动克隆。
2. 性能考虑
Clone
:如果你的类型包含大量数据或复杂的数据结构,自动派生的Clone
实现可能会进行深拷贝,这可能导致不必要的性能开销。在这种情况下,可能需要手动实现更高效的克隆逻辑。
3. 逻辑复杂性或特定行为
PartialEq
和Eq
:如果你需要基于非直观的条件比较两个对象的相等性(例如,忽略某些字段或基于运行时状态),自动派生的PartialEq
和Eq
可能不符合你的需求。在这种情况下,你需要手动实现这些 trait,以精确控制相等性检查的行为。
4. 安全性和封装
- 自动派生的 trait 实现可能会无意中暴露内部状态,这可能与你的封装策略相冲突。例如,如果你不希望外部代码能够比较或打印内部状态,那么自动派生 Debug 或 PartialEq 可能是不合适的。
举个🌰:
程序👇:
struct A;
#[derive(Debug)]
pub enum E {
A(A), //在这里用了一个自定义的结构体 A
B(String),
}
fn main() {
let a = E::A(A);
println!("{:?}",a);
let b = E::B("B".to_string());
println!("{:?}",b);
}
cargo build
输出👇:
error[E0277]: `A` doesn't implement `Debug`
--> my_test\src\main.rs:5:5
|
3 | #[derive(Debug)]
| ----- in this derive macro expansion
4 | pub enum E {
5 | A(A),
| ^ `A` cannot be formatted using `{:?}`
为 A
实现 Debug
trait 👇:
use std::fmt;
use std::fmt::{Display, Formatter};
pub struct A;
// 为A实现Debug trait
impl fmt::Debug for A {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt("出来吧!我的 A", f) // 输出定制
}
}
#[derive(Debug)]
pub enum E {
A(A), //在这里用了一个自定义的结构体 A
B(String),
}
fn main() {
let a = E::A(A);
println!("{:?}",a);
let b = E::B("B".to_string());
println!("{:?}",b);
}
cargo run
输出👇:
A(出来吧!我的 A)
B("B")
其实还可以更简单:
#[derive(Debug)]
pub struct A;
#[derive(Debug)]
pub enum E {
A(A),
B(String),
}
fn main() {
let a = E::A(A);
println!("{:?}",a);
let b = E::B("B".to_string());
println!("{:?}",b);
}
只不过输出的很简单👇:
A(A)
B("B")
🗝️很显然这样你无法召唤你的 A
出来😜😜
本站中有一部分来源于网络和媒体的内容(文章、源码、软件应用、资源附件等),并尽可能的标出参考来源、出处,本站尊重原作者的成果,若本站内容侵犯了您的合法权益时或者对转载内容有疑义的内容原作者,请书面反馈并提供确切的个人身份证明与详细资料信息在第一时间以邮件形式进行联系沟通;