类型、分类定义时使用 type 还是 kind ?

在编程设计中对 分类类型状态 进行设计时,很多时候选择的单词本身并不能体现现分类、类型的含义,比如:ErrorMessage,那这时我们就会加上 Type 后缀,对应的字段通知就会使用 type 进行命名。比如在定义消息体时:

1
2
3
4
pub struct MessagePayload {
pub type: MessageType,
pub content: String,
}

但是,type 在 Rust 中是关键字,这里将字段命名为 type 是不合法的,可以改为 msg_type。那有其它可替代 type 的单词吗(假定我们在编程中通常使用英语单词进行命名),那就是 kind。后续本文将较为详细的讨论在编程设计中什么时候使用 Type,而又在什么时候合适使用 Kind

英语语言分析:kind vs type

核心语义差异

维度 kind type
本质 自然属性/内在分类 技术规格/系统分类
来源 自然形成/本质特征 人为定义/技术标准
范围 宽泛类别 (what sort of) 精确类型 (what specific)
示例 “What kind of fruit?” “What type of file?”
适用域 生物/行为/抽象概念 技术/系统/协议规范

编程场景适用原则

适合用 Kind 的场景:

  1. 自然属性分类:错误类型、用户状态、生物类别
  2. 行为特征:操作类型、事件类别
  3. 本质差异:编译器标记、语法元素
  4. 宽泛分类:需要后续细分的顶层类别

适合用 Type 的场景:

  1. 技术规范:协议格式、硬件类型
  2. 系统标准:文件格式、数据类型
  3. 精确规格:消息协议、API 版本
  4. 人为定义:业务逻辑中的分类标准

📌 核心经验法则:
当分类反映 内在本质 时用 Kind,反映 技术规范 时用 Type


编程语言实现方案

Rust 最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 不加场景(`Status` 状态已隐含分类的含义)
#[derive(Debug)]
enum UserStatus {
Active, // 用户自然状态
Suspended, // 系统干预状态
Deleted, // 用户本质状态
}

// 适合用 Kind 的场景 (自然属性)
#[derive(Debug)]
enum ErrorKind {
NotFound, // 错误本质
Unauthorized, // 权限本质
Timeout, // 网络本质
}

// 适合用 Type 的场景 (技术规范)
#[derive(Debug)]
enum DeviceType {
SSD, // 硬件技术类型
HDD, // 存储技术规格
NVMe, // 接口技术标准
}

// 适合用 Type 的场景 (技术规范,协议)
#[derive(Debug)]
enum MsgType {
Text, // 协议消息类型
Binary, // 数据传输格式
Control, // 系统控制信号
}

// 结构体字段处理方案
struct NetworkPacket {
packet_kind: PacketKind, // 包的本质类别
packet_type: PacketType, // 包的技术格式
size: usize,
}

// 关键字规避技巧
struct DatabaseRecord {
record_type: RecordType, // 使用完整词避免关键字
content: Vec<u8>,
}

TypeScript 最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 不加场景(`Status` 状态已隐含分类的含义)
enum UserStatus {
Active = "active", // 用户自然状态
Suspended = "suspended", // 账户本质状态
Deleted = "deleted", // 永久状态
}

// 适合用 Kind 的场景 (自然属性)
enum ErrorKind {
Network = "network", // 错误根源类别
Validation = "validation", // 数据本质问题
Auth = "authentication", // 权限本质问题
}

// 适合用 Type 的场景
enum MessageType {
Text = "text", // 协议消息类型
Image = "image", // 媒体技术格式
Video = "video", // 传输数据类型
}

enum FileType {
PDF = "application/pdf", // MIME技术类型
JSON = "application/json",
CSV = "text/csv",
}

// 接口字段处理方案
interface APIResponse {
statusKind: ResponseStatusKind; // 响应本质状态
contentType: ContentType; // 技术格式类型
data: unknown;
}

// 关键字规避技巧
class NetworkMessage {
msgType: MessageType; // 使用缩写msgType替代type

constructor(
public readonly kind: MessageKind, // 使用kind
public readonly type: MessageType // TS允许但需谨慎
) {}
}

使用 serde 自定义序列化时的名称

在 Rust 中可以使用 serdeserde_jsonty 字段序列化为 type 字段。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
use serde::{Serialize, Deserialize};
use serde_json;

// 适合用 "type" 的场景(技术规格)
#[derive(Debug, Serialize, Deserialize)]
enum MessageType {
Text,
Image,
Video,
}

// 使用 serde 的 rename 属性解决关键字问题
#[derive(Debug, Serialize, Deserialize)]
struct NetworkMessage {
id: u32,
content: String,

#[serde(rename = "type")] // 关键注解:序列化为 "type"
ty: MessageType, // 内部使用非关键字字段名
}

// 适合用 "kind" 的场景(本质属性)
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
enum ErrorKind {
Timeout,
InvalidInput,
NotFound,
}

fn main() {
// 序列化示例 (ty → type)
let msg = NetworkMessage {
id: 42,
content: "Hello world".into(),
ty: MessageType::Image,
};

let json = serde_json::to_string_pretty(&msg).unwrap();
println!("Serialized:\n{}\n", json);
/* 输出:
{
"id": 42,
"content": "Hello world",
"type": "Image" // 注意这里是 type 而不是 ty
}
*/

// 反序列化示例 (type → ty)
let json_data = r#"
{
"id": 99,
"content": "Goodbye",
"type": "Text"
}"#;

let decoded: NetworkMessage = serde_json::from_str(json_data).unwrap();
println!("Deserialized: {:?}", decoded);
// 输出: NetworkMessage { id: 99, content: "Goodbye", ty: Text }
}

关键解决方案说明

  1. #[serde(rename = "type")] 属性

    • 将结构体字段 ty 序列化为 JSON 的 type 字段
    • 反序列化时自动将 JSON 的 type 映射回 ty 字段
  2. 字段命名策略

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 推荐方案:
    struct NetworkMessage {
    #[serde(rename = "type")]
    ty: MessageType, // 清晰且避免关键字
    }

    // 替代方案(不推荐):
    struct NetworkMessage {
    #[serde(rename = "type")]
    r#type: MessageType, // 使用原始标识符语法,可读性差
    }
  3. 枚举序列化优化

    1
    2
    3
    4
    5
    6
    7
    8
    // 默认使用变体名(如 "Text")
    #[derive(Serialize, Deserialize)]
    enum MessageType { Text, Image }

    // 可选:序列化为小写蛇形命名
    #[derive(Serialize, Deserialize)]
    #[serde(rename_all = "snake_case")]
    enum MessageType { Text, Image } // 变为 "text", "image"

Typescript 对比

typescript 中,可以使用 type 作为类型的字段命名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 类型定义
enum MessageType {
Text = "text",
Image = "image",
Video = "video",
}

interface NetworkMessage {
id: number;
content: string;
type: MessageType; // TS 中可直接用 type
}

// 序列化
const msg: NetworkMessage = {
id: 42,
content: "Hello TS",
type: MessageType.Image,
};

console.log(JSON.stringify(msg));
// 输出: {"id":42,"content":"Hello TS","type":"image"}

// 反序列化
const jsonStr = '{"id":99,"content":"Goodbye","type":"text"}';
const decoded: NetworkMessage = JSON.parse(jsonStr);

使用场景小结

场景 Rust 方案 TypeScript 方案
技术规格分类 #[serde(rename = "type")] ty: T 直接使用 type: T
本质属性分类 直接使用 kind: ErrorKind 直接使用 kind: ErrorKind
规避关键字 ty + rename 属性 无需特殊处理
枚举序列化 #[serde(rename_all = "...")] 原生支持字符串枚举

最佳实践建议:

  1. 对技术规格类字段使用 ty + #[serde(rename = “type”)]
  2. 对本质属性类字段直接使用 kind
  3. 始终在 JSON 层保持语义正确的字段名(如 type)
  4. 在代码层使用非关键字字段名(如 ty)确保编译通过

总结: 跨语言通用准则

优先使用 Kind 的情况

  1. 状态/错误分类UserStatusKind, ErrorKind
  2. 自然属性AnimalKind, MaterialKind
  3. 行为模式InteractionKind, EventKind
  4. 编译器内部TokenKind, NodeKind

优先使用 Type 的情况

  1. 协议/格式MsgType, FileType
  2. 硬件规范DeviceType, CpuType
  3. 数据传输PayloadType, EncodingType
  4. 业务标准InvoiceType, PaymentType

关键字处理策略

语言 推荐方案 备选方案
Rust msg_type/record_type ty/kind
TypeScript msgType/contentType kind/variant
Go msgType/contentType kind/cat

💡 黄金法则
当不确定时,选择 Kind 更安全(80%场景适用),
仅在明确技术规范时使用 Type(需处理关键字问题)

分享到