首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >25-Rust 教程 - 高级类型

25-Rust 教程 - 高级类型

作者头像
LarryLan
发布2026-05-29 13:08:33
发布2026-05-29 13:08:33
540
举报

高级类型

newtype、never 类型:类型系统还能这么玩?

🎬 引入

Rust 的类型系统,表面上看着挺正常:整数、字符串、结构体、枚举...

但深入之后你会发现:这玩意儿能玩出花来。

  • newtype 模式:给现有类型穿个"马甲"
  • 类型别名:给长名字起个"小名"
  • never 类型:一个永远不存在的类型
  • Sized 特质:决定类型能不能上栈

我第一次看到 ! 这个类型时,整个人都懵了:"这是个啥?感叹号?"

后来才知道,这玩意儿叫 never type,代表"永远不会返回"。今天咱们就来扒一扒 Rust 类型系统里这些"高级玩家"。

📌 核心概念

newtype 模式:给类型穿马甲

newtype 模式:用元组结构体包装现有类型,获得类型安全。

为什么需要? 看个例子:

代码语言:javascript
复制
// ❌ 没有类型安全
fn login(username: String, password: String) {}
fn register(username: String, password: String) {}

fn main() {
    let user = String::from("larry");
    let pass = String::from("123456");
    
    // ❌ 参数顺序错了,但编译器不报错!
    login(pass, user);  // 密码当用户名传了...
}

用 newtype 解决:

代码语言:javascript
复制
// ✅ 类型安全
struct Username(String);
struct Password(String);

fn login(username: Username, password: Password) {}
fn register(username: Username, password: Password) {}

fn main() {
    let user = Username(String::from("larry"));
    let pass = Password(String::from("123456"));
    
    // ❌ 现在顺序错了会编译错误!
    // login(pass, user);  // 类型不匹配!
    
    login(user, pass);  // ✅ 正确
}

newtype 的优势:

  1. 类型安全:编译器帮你检查
  2. 零开销:编译后和直接用一个样
  3. 可以添加方法:给包装类型加专属方法
  4. 隐藏实现细节:外面不知道里面是啥

类型别名:给长名字起小名

类型别名:给现有类型起个"小名",不是新类型。

代码语言:javascript
复制
// 长名字
type Result<T> = std::result::Result<T, std::io::Error>;

// 用起来
fn read_file() -> Result<String> {
    // ...
}

类型别名 vs newtype:

特性

类型别名

newtype

类型检查

和原类型一样

独立类型

开销

添加方法

不能

用途

简化名字

类型安全

代码语言:javascript
复制
// 类型别名
type Meters = f64;
type Kilometers = f64;

fn main() {
    let m: Meters = 1000.0;
    let k: Kilometers = 1.0;
    
    // ❌ 可以相加,但可能没意义
    let sum = m + k;  // 编译器不管
}

// newtype
struct Meters(f64);
struct Kilometers(f64);

fn main() {
    let m = Meters(1000.0);
    let k = Kilometers(1.0);
    
    // ❌ 不能相加,类型不同
    // let sum = m + k;  // 编译错误!
}

never 类型:永远不返回

never 类型!,表示"永远不会返回"。

哪些函数返回 !

  1. panic!() - 程序崩溃
  2. loop 无限循环(没有 break)
  3. std::process::exit() - 退出程序
代码语言:javascript
复制
fn crash() -> ! {
    panic!("boom!");
}

fn forever() -> ! {
    loop {
        // 永远循环
    }
}

never 类型的特点:

  • 可以转换成任何类型
  • 用在"不会执行到"的地方
代码语言:javascript
复制
let guess: u32 = match some_result {
    Ok(v) => v,
    Err(_) => panic!("出错了!"),  // panic! 返回 !,可以当 u32 用
};

为什么可以? 因为 ! 永远不会真正返回值,所以它可以假装是任何类型。

Sized 特质:类型能不能上栈

Sized 特质:标记类型在编译时是否有固定大小。

规则:

  • 大多数类型都是 Sized 的(i32String、结构体等)
  • 有些类型不是 Sized 的(str[T]dyn Trait
代码语言:javascript
复制
// ❌ 不能这样
let s: str = "hello";  // str 不是 Sized

// ✅ 要这样
let s: &str = "hello";  // &str 是 Sized(引用有固定大小)

Sized 作为泛型约束:

代码语言:javascript
复制
// 泛型参数默认是 Sized 的
fn generic<T>(x: T) {}  // 相当于 fn generic<T: Sized>(x: T) {}

// 如果不是 Sized,要显式标注
fn not_sized<T: ?Sized>(x: &T) {}  // ?Sized 表示可以是也可以不是

?Sized 的含义:

  • T: Sized → T 必须是 Sized 的
  • T: ?Sized → T 可以是 Sized 的,也可以不是
代码语言:javascript
复制
// 可以接受 str 和 String
fn print_it<T: ?Sized>(x: &T) 
where
    T: std::fmt::Display 
{
    println!("{}", x);
}

fn main() {
    print_it("hello");      // &str
    print_it(&String::from("hello"));  // &String
}

💻 代码示例

示例 1:newtype 模式基础

代码语言:javascript
复制
// 包装类型
struct Meters(f64);
struct Seconds(f64);

impl Meters {
    fn new(value: f64) -> Self {
        Meters(value)
    }
    
    fn to_kilometers(&self) -> Kilometers {
        Kilometers(self. / 1000.0)
    }
}

impl Seconds {
    fn new(value: f64) -> Self {
        Seconds(value)
    }
    
    fn to_minutes(&self) -> Minutes {
        Minutes(self. / 60.0)
    }
}

struct Kilometers(f64);
struct Minutes(f64);

fn main() {
    let distance = Meters::new(1500.0);
    let time = Seconds::new(120.0);
    
    println!("距离:{:?} 米", distance.);
    println!("距离:{:?} 公里", distance.to_kilometers().);
    println!("时间:{:?} 秒", time.);
    println!("时间:{:?} 分钟", time.to_minutes().);
    
    // ❌ 不能混用
    // let speed = distance.0 / time.0;  // 类型不匹配
}

示例 2:newtype 实现 Trait

代码语言:javascript
复制
use std::fmt;

struct Wrapper(Vec<String>);

impl fmt::Display for Wrapper {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[{}]", self..join(", "))
    }
}

fn main() {
    let w = Wrapper(vec![
        String::from("hello"),
        String::from("world"),
    ]);
    
    println!("w = {}", w);  // 输出:w = [hello, world]
}

示例 3:类型别名实战

代码语言:javascript
复制
// 简化复杂类型
type Thunk = Box<dyn Fn() + Send + 'static>;

fn main() {
    let f: Thunk = Box::new(|| {
        println!("hello");
    });
    
    f();
}

// 带错误的 Result
type IoResult<T> = Result<T, std::io::Error>;

fn read_file(path: &str) -> IoResult<String> {
    std::fs::read_to_string(path)
}

示例 4:never 类型应用

代码语言:javascript
复制
fn main() {
    // match 表达式中的 never 类型
    let result: Result<i32, &str> = Ok();
    
    let value = match result {
        Ok(v) => v,
        Err(_) => panic!("出错了!"),  // ! 可以当 i32 用
    };
    
    println!("值:{}", value);
    
    // 无限循环
    let mut count = ;
    let x: i32 = loop {
        count += ;
        if count ==  {
            break count * ;  // 返回 i32
        }
    };
    
    println!("x = {}", x);
}

示例 5:?Sized 约束

代码语言:javascript
复制
use std::fmt::Display;

// 可以接受 Sized 和 ?Sized 类型
fn print_it<T: ?Sized>(x: &T) 
where
    T: Display 
{
    println!("{}", x);
}

// 只能接受 Sized 类型
fn print_sized<T: Display>(x: T) {
    println!("{}", x);
}

fn main() {
    // &str 不是 Sized,但 &(&str) 是
    print_it("hello");  // ✅
    // print_sized("hello");  // ✅ "hello" 是 &str,是 Sized 的
    
    // dyn Trait 不是 Sized
    let boxed: Box<dyn Display> = Box::new();
    print_it(&*boxed);  // ✅
    // print_sized(*boxed);  // ❌
}

示例 6:newtype 用于 API 设计

代码语言:javascript
复制
// 用 newtype 防止 API 误用
struct Email(String);
struct Password(String);

impl Email {
    fn new(email: &str) -> Result<Self, &'static str> {
        if email.contains('@') {
            Ok(Email(email.to_string()))
        } else {
            Err("无效的邮箱地址")
        }
    }
    
    fn as_str(&self) -> &str {
        &self.
    }
}

impl Password {
    fn new(password: &str) -> Result<Self, &'static str> {
        if password.len() >=  {
            Ok(Password(password.to_string()))
        } else {
            Err("密码至少 8 位")
        }
    }
}

struct User {
    email: Email,
    password: Password,
}

fn main() {
    let email = Email::new("larry@example.com").unwrap();
    let password = Password::new("secure123").unwrap();
    
    let user = User {
        email,
        password,
    };
    
    println!("用户邮箱:{}", user.email.as_str());
    
    // ❌ 不能直接用字符串
    // let bad_user = User {
    //     email: "bad@email",  // 类型不匹配
    //     password: "123",     // 类型不匹配
    // };
}

错误示例:混淆类型别名和 newtype

代码语言:javascript
复制
// ❌ 错误理解
type Meters = f64;
type Kilometers = f64;

fn add_distance(a: Meters, b: Kilometers) -> f64 {
    a + b  // 可以相加,但语义上可能不对
}

// ✅ 正确:用 newtype
struct Meters2(f64);
struct Kilometers2(f64);

// fn add_distance2(a: Meters2, b: Kilometers2) -> ??? {
//     a.0 + b.0  // 需要显式解包
// }

🐛 常见坑点

坑点 1:newtype 忘记解包

代码语言:javascript
复制
struct Wrapper(i32);

fn main() {
    let w = Wrapper();
    
    // ❌ 不能直接用
    // let sum = w + 10;
    
    // ✅ 要解包
    let sum = w. + ;
}

坑点 2:never 类型误解

代码语言:javascript
复制
fn main() {
    // ❌ 不能创建 ! 类型的值
    // let x: !;
    
    // ✅ ! 只能作为返回值
    fn crash() -> ! {
        panic!();
    }
}

坑点 3:?Sized 忘记加引用

代码语言:javascript
复制
fn print_it<T: ?Sized>(x: &T) {}

fn main() {
    let s = "hello";
    
    print_it(s);  // ✅ s 是 &str
    
    // ❌ 不能传值
    // print_it("hello");  // 字面量是 &'static str
}

🎯 实战案例

案例 1:类型安全的 ID 系统

代码语言:javascript
复制
// 用 newtype 区分不同类型的 ID
struct UserId(u64);
struct PostId(u64);
struct CommentId(u64);

impl UserId {
    fn new() -> Self {
        UserId(rand::random())
    }
}

struct User {
    id: UserId,
    name: String,
}

struct Post {
    id: PostId,
    author: UserId,  // 明确是 UserId
    content: String,
}

fn get_user_posts(user_id: UserId, posts: &[Post]) -> Vec<&Post> {
    posts
        .iter()
        .filter(|p| p.author == user_id)
        .collect()
}

fn main() {
    let user = User {
        id: UserId::new(),
        name: String::from("Larry"),
    };
    
    let posts = vec![
        Post {
            id: PostId(),
            author: user.id,
            content: String::from("Hello"),
        },
    ];
    
    let user_posts = get_user_posts(user.id, &posts);
    println!("用户有 {} 篇帖子", user_posts.len());
    
    // ❌ 不能混用 ID 类型
    // get_user_posts(PostId(1), &posts);  // 编译错误!
}

案例 2:单位转换系统

代码语言:javascript
复制
// 用 newtype 实现类型安全的单位转换
#[derive(Debug, Clone, Copy)]
struct Meters(f64);

#[derive(Debug, Clone, Copy)]
struct Feet(f64);

impl Meters {
    fn to_feet(&self) -> Feet {
        Feet(self. * 3.28084)
    }
}

impl Feet {
    fn to_meters(&self) -> Meters {
        Meters(self. / 3.28084)
    }
}

// 实现加法(同单位)
impl std::ops::Add for Meters {
    type Output = Meters;
    
    fn add(self, other: Meters) -> Meters {
        Meters(self. + other.)
    }
}

fn main() {
    let a = Meters(100.0);
    let b = Meters(50.0);
    
    let sum = a + b;
    println!("总和:{:?} 米", sum);
    
    let in_feet = sum.to_feet();
    println!("总和:{:?} 英尺", in_feet);
    
    // ❌ 不能直接相加不同单位
    // let wrong = Meters(100.0) + Feet(50.0);
}

案例 3:配置类型安全

代码语言:javascript
复制
// 用 newtype 防止配置错误
struct Port(u16);
struct Host(String);
struct Timeout(std::time::Duration);

impl Port {
    fn new(port: u16) -> Result<Self, &'static str> {
        if port ==  || port >  {
            Err("无效端口")
        } else {
            Ok(Port(port))
        }
    }
}

struct DatabaseConfig {
    host: Host,
    port: Port,
    timeout: Timeout,
}

fn connect(config: &DatabaseConfig) {
    println!(
        "连接到 {}:{},超时 {:?}",
        config.host.,
        config.port.,
        config.timeout.
    );
}

fn main() {
    let config = DatabaseConfig {
        host: Host(String::from("localhost")),
        port: Port::new().unwrap(),
        timeout: Timeout(std::time::Duration::from_secs()),
    };
    
    connect(&config);
    
    // ❌ 类型安全防止错误
    // let bad_config = DatabaseConfig {
    //     host: Host(String::from("localhost")),
    //     port: Port(99999),  // 超出范围,但构造函数会检查
    //     timeout: Timeout(std::time::Duration::from_secs(30)),
    // };
}

🧠 思维导图

25-高级类型
25-高级类型

📝 小结

核心要点:

  1. newtype 模式:用元组结构体包装,类型安全零开销
  2. 类型别名:起小名,不是新类型,不能加方法
  3. never 类型! 表示永不返回,可以转任何类型
  4. Sized 特质:标记编译时固定大小的类型
  5. ?Sized:允许非 Sized 类型,需要配合引用使用

选择指南:

  • 需要类型安全 → newtype
  • 只是简化名字 → 类型别名
  • 永不返回的函数 → !
  • 泛型接受切片/str → ?Sized

下篇预告:

类型系统玩得差不多了,咱们来聊聊 Rust 里最让人头大的生命周期进阶。多个生命周期怎么搞?'static 是啥?trait 对象里的生命周期怎么处理?下篇一起攻克这个难关!

互动问题:

你觉得 newtype 模式有用吗?还是觉得太麻烦?有没有被 ?Sized 坑过?评论区聊聊!

🔗 参考资料

  • Rust Book - Advanced Types
  • Newtype Pattern
  • Never Type
  • Sized Trait
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-05-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Larry的Hub 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 高级类型
    • 🎬 引入
    • 📌 核心概念
      • newtype 模式:给类型穿马甲
      • 类型别名:给长名字起小名
      • never 类型:永远不返回
      • Sized 特质:类型能不能上栈
    • 💻 代码示例
      • 示例 1:newtype 模式基础
      • 示例 2:newtype 实现 Trait
      • 示例 3:类型别名实战
      • 示例 4:never 类型应用
      • 示例 5:?Sized 约束
      • 示例 6:newtype 用于 API 设计
      • 错误示例:混淆类型别名和 newtype
    • 🐛 常见坑点
      • 坑点 1:newtype 忘记解包
      • 坑点 2:never 类型误解
      • 坑点 3:?Sized 忘记加引用
    • 🎯 实战案例
      • 案例 1:类型安全的 ID 系统
      • 案例 2:单位转换系统
      • 案例 3:配置类型安全
    • 🧠 思维导图
    • 📝 小结
    • 🔗 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档