当前位置:

首页 > 编程开发 > Rust对象中的结构体、枚举、特性详解

Rust对象中的结构体、枚举、特性详解

本文目录

    1 结构体 先从结构体说起。Rust 把数据组织成整体有两种方式:元组和结构体。元组灵活但没名字,结构体给每个字段起了名字,用起来更直观。结构体在代码里很常见,用来定义常用的数据结构,每个成员叫“字段”。 1.1 简介 1.1.1 定义 Rust 的结构体与元组都可以把一堆类型不同的数据捆绑成整体,

    1 结构体

    先从结构体说起。Rust 把数据组织成整体有两种方式:元组和结构体。元组灵活但没名字,结构体给每个字段起了名字,用起来更直观。结构体在代码里很常见,用来定义常用的数据结构,每个成员叫“字段”。

    1.1 简介

    1.1.1 定义

    Rust 的结构体与元组都可以把一堆类型不同的数据捆绑成整体,但结构体给每个成员起了名字,访问的时候不用记下标。元组适合临时传递多个值,结构体适合长期使用的数据结构。结构体的每个成员叫作“字段”。

    Rust对象中的结构体、枚举、特性详解

    定义结构体时,注意结尾不需要 ;,每个字段后面用逗号分隔:

    struct Site {
        domain: String,
        name: String,
        nation: String,
        found: u32
    }

    1.1.2 结构体实例

    Rust 在语法上借鉴了不少 JavaScript 的习惯,实例化结构体用的是类似 JSON 的 key: value 语法:

    let runoob = Site {
        domain: String::from("www.baidu.com"),
        name: String::from("test"),
        nation: String::from("China"),
        found: 2013
    };

    这种写法不仅直观,而且字段顺序可以随意。如果字段名和已有变量名相同,可以简写:

    let domain = String::from("www.baidu.com");
    let name = String::from("test");
    let test = Site {
        domain,  // 等同于 domain : domain,
        name,    // 等同于 name : name,
        nation: String::from("China"),
        found: 2026
    };

    想要基于现有实例创建新实例,只改几个字段,可以用结构体更新语法:

    let site = Site {
        domain: String::from("www.baidu.com"),
        name: String::from("test"),
        ..test 
    };

    这里有个小细节:..test 后面不能加逗号,而且至少得重新设定一个字段的值,不能完全复制。

    1.1.3 元组结构体

    如果觉得给每个字段起名字太麻烦,元组结构体是个不错的选择——它没有字段名,只有类型,形式上像元组。适合处理那些需要定义类型但又不想太复杂的简单数据。访问方式也和元组一样,通过 . 加下标:

    fn main() {
        struct Color(u8, f64, u8);
        let black = Color(0, 0.0, 0);
        println!("black = ({}, {}, {})", black.0, black.1, black.2);
    }

    1.2 结构体所有权

    1.2.1 输出结构体

    调试的时候,能一眼看到整个结构体实例的内容太方便了。手动写格式化代码又烦又容易错,Rust 贴心地提供了 #[derive(Debug)] 这个宏,加上之后用 {:?} 就能直接打印:

    #[derive(Debug)]
    struct Rectangle {
        width: u32,
        height: u32,
    }
    fn main() {
        let rect1 = Rectangle { width: 30, height: 50 };
        println!("rect1 is {:?}", rect1);
    }

    之后在 println! 和 print! 宏中就可以用 {:?} 占位符输出整个结构体。

    1.2.2 结构体方法

    方法(Method)和函数类似,但它是用来操作结构体实例的。Rust 不是面向对象语言,所有权机制就是证明,但它还是提供了方法。第一个参数必须是 &self,而且调用时不用传 self。注意 self 是关键字,不是风格。

    struct Rectangle {
        width: u32,
        height: u32,
    }
    impl Rectangle {
        fn area(&self) -> u32 {
            self.width * self.height
        }
        fn wider(&self, rect: &Rectangle) -> bool {
            self.width > rect.width
        }
    }
    fn main() {
        let rect1 = Rectangle { width: 30, height: 50 };
        let rect2 = Rectangle { width: 40, height: 20 };
    	println!("rect1's area is {}", rect1.area());	
        println!("{}", rect1.wider(&rect2));
    }
    运行结果:false

    在 Rust 中,self 和 Self 仅差一个字母大小写,但含义完全不同:

    selfSelf
    含义方法首参数的变量名(当前实例本身)类型本身的别名
    类比类似 Java 中的 this类似 Java 中的"当前类名"
    位置出现在方法的第一个参数出现在返回类型、字段类型等位置
    本质一个绑定到实例的变量一个类型

    Self 只能在 impl 块或 trait 定义内部使用。在普通函数顶层,得写具体类型名。

    1.2.3 结构体关联函数

    没有 &self 参数的函数叫关联函数,类似静态方法。它不依赖实例,但使用它需要声明是在哪个 impl 块中的。String::from 就是一个典型的关联函数:

    #[derive(Debug)]
    struct Rectangle {
        width: u32,
        height: u32,
    }
    impl Rectangle {
        fn create(width: u32, height: u32) -> Rectangle {
            Rectangle { width, height }
        }
    }
    fn main() {
        let rect = Rectangle::create(30, 50);
        println!("{:?}", rect);
    }
    运行结果:
    Rectangle { width: 30, height: 50 }

    结构体的 impl 块可以写多次,效果相当于它们内容的拼接。

    1.2.4 单元结构体

    单元结构体(Unit Struct)没有任何字段,它只是一个“类型标记”,实例本身不占实际数据空间(编译后大小为 0)。常用于:只需要一个类型,但不需要存储任何数据的场景。

    struct UnitStruct;
    fn main() {
        let u = UnitStruct;  // 创建实例,什么数据都没有
    }

    和其他结构体的对比:

    struct UnitStruct;                    // 无字段
    struct TupleStruct(i32, i32);         // 有字段,无字段名
    struct NormalStruct { x: i32 }		  // 有字段,有字段名

    三者的区别就在于有没有数据、数据有没有名字。单元结构体是最简单的一种——纯粹作为一个类型存在,用来实现某个 trait 或者做类型标记,而不携带任何数据。

    2 枚举类

    2.1 简介

    Rust 的枚举比很多语言的 enum 强大得多——每个变体都能带数据,而且可以带不同类型的数据,甚至三种形式混着用:无数据、元组式数据、结构体式数据。

    enum Message {
        Quit,                // 无数据(类似单元结构体)
        Move { x: i32, y: i32 },     // 结构体式:字段有名字
        Write(String),               // 元组式:只有类型,按位置
        ChangeColor(i32, i32, i32),  // 元组式,多个值
    }

    简单使用示例:

    fn main() {
       let msgs = vec![
           Message::Quit,
           Message::Move { x: 10, y: 20 },
           Message::Write(String::from("hello")),
           Message::ChangeColor(255, 0, 0),
       ];
       for msg in msgs {
           match msg {
               Message::Quit => {
                   println!("退出");
               }
               Message::Move { x, y } => {
                   println!("移动到 ({}, {})", x, y);  // 按字段名解构
               }
               Message::Write(text) => {
                   println!("写入: {}", text);// 按位置解构
               }
               Message::ChangeColor(r, g, b) => {
                   println!("颜色: ({}, {}, {})", r, g, b);
               }
           }
       }
    }

    枚举变体也能有方法:

    impl Message {
        fn call(&self) {
            match self {
                Message::Write(text) => println!("{}", text),
                _ => println!("其他消息"),
            }
        }
    }
    let m = Message::Write(String::from("hi"));
    m.call();  // hi

    2.2 match 语法

    2.2.1 简介

    Rust 通过 match 语句实现分支结构。先看看如何用 match 处理枚举:

    fn main() {
        enum Book {
            Papery {index: u32},
            Electronic {url: String},
        }  
        let book = Book::Papery{index: 1001};
        let ebook = Book::Electronic{url: String::from("url...")};   
        match book {
            Book::Papery { index } => {
                println!("Papery book {}", index);
            },
            Book::Electronic { url } => {
                println!("E-book {}", url);
            }
        }
    }

    match 块也可以当作表达式使用,有返回值,但所有分支返回值的类型必须一样:

    match 枚举类实例 {
        分类1 => 返回值表达式,
        分类2 => 返回值表达式,
        ...
    }

    对于枚举变体中元组数据,在 match 块中需要临时指定一个名字;对于结构体变体,可以通过 字段名:变量名 重命名:

    enum Book {
        Papery(u32),
        Electronic {url: String},
    }
    let book = Book::Papery(1001);
    match book {
        Book::Papery(i) => { //临时名字
            println!("{}", i);
        },
        Book::Electronic { url } => { //使用原来名字
            println!("{}", url);
        }
        // 或者显式重命名:字段名:变量名
        Book::Electronic { url: link } => {
            println!("{}", link);  // 变量名是 link
        },
        // 甚至可以忽略值
        Book::Electronic { url: _ } => {
            println!("has url but ignored");
        },
    }

    对非枚举类型进行分支选择时,必须处理例外情况,用下划线 _ 表示:

    fn main() {
        let t = "abc";
        match t {
            "abc" => println!("Yes"),
            _ => {},
        }
    }

    2.2.2 ref 关键字

    ref 用在 match 模式里,按引用绑定,防止所有权被 move 走:

    let opt = Some(String::from("hello"));
    match opt {
        Some(ref s) => println!("{}", s),  // ref:s 是 &String,内容的所有权不动
        None => {}
    }
    println!("{:?}", opt);  //  opt 还能用——如果没写 ref,String 早被 move 进分支了

    为什么需要 ref?因为 match 默认按值匹配,会把字段 move 进绑定变量;加 ref 改成“绑定一个指向字段的引用”,原值所有权保留。对应的还有 ref mut(绑定 &mut)。

    不过现代 Rust 里 ref 基本绝迹了,因为 2018 版引入了 match ergonomics(匹配人体工学):当 match 一个引用时,绑定模式会自动降级成引用,不用手写 ref:

    let opt = Some(String::from("hello"));
    match &opt {           // match 的是引用
        Some(s) => println!("{}", s),  // s 自动就是 &String,不用 ref
        None => {}
    }
    println!("{:?}", opt); // ✅ 一样不 move

    2.3 if let 语法

    if let 语法格式:

    if let 模式(枚举变体) = 源变量 {
        语句块
    }

    注意:这里是一个等号,不是两个等号,因为做的是模式绑定,不是相等比较。if let 本质上是 let 的延伸,尝试做解构绑定(把复合值拆开,把里面的各个部分分别绑定到新变量上)。

    使用示例:

    let i = 0;
    match i {
        0 => println!("zero"),
        _ => {},
    }
    放入主函数运行结果:zero
    // 现在用 if let 语法缩短这段代码:
    let i = 0;
    if let 0 = i {
        println!("zero");
    }

    可以在后面加一个 else 块处理例外情况。if let 可以看作是只区分两种情况的 match 的语法糖。

    对于枚举类依然适用:

    fn main() {
        enum Book {
            Papery(u32),
            Electronic(String)
        }
        let book = Book::Electronic(String::from("url"));
        if let Book::Papery(index) = book {
            println!("Papery {}", index);
        } else {
            println!("Not papery book");
        }
    }

    2.4 Option 枚举类

    Option 是 Rust 标准库中的枚举类,用于填补 Rust 不支持 null 引用的空白。Rust 在语言层面彻底不允许空值,但无奈 null 能高效解决少量问题,所以引入了 Option:

    enum Option {
        Some(T),
        None,
    }

    如果要定义一个可以为空值的变量,可以这样:let opt = Option::Some("Hello");。如果想针对 opt 执行操作,必须先判断它是否是 None:

    fn main() {
        let opt = Option::Some("Hello");
        match opt {
            Option::Some(something) => {
                println!("{}", something);
            },
            Option::None => {
                println!("opt is nothing");
            }
        }
    }

    初始值为空的 Option 必须明确类型:

    fn main() {
        let opt: Option<&str> = Option::None;
        match opt {
            Option::Some(something) => {
                println!("{}", something);
            },
            Option::None => {
                println!("opt is nothing");
            }
        }
    }
    运行结果:opt is nothing

    这种设计让空值编程变得不容易,但正是构建稳定高效系统所需要的。由于 Option 是编译器默认引入的,使用时可以省略 Option::,直接写 None 或 Some()。Option 是一种特殊的枚举类,可以含值分支选择:

    fn main() {
            let t = Some(64);
            match t {
                    Some(64) => println!("Yes"),
                    _ => println!("No"),
            }
    }

    3 特性

    3.1 简介

    特性(trait)有点像 Java 的接口,但更灵活。它们都是一种行为规范,可以标识哪些类有哪些方法。特性在 Rust 中用 trait 表示:

    trait Descriptive {
        fn describe(&self) -> String;
    }

    用特性实现一个结构体,格式是 impl <特性名> for <所实现的类型名>:

    struct Person {
        name: String,
        age: u8
    }
    impl Descriptive for Person {
        fn describe(&self) -> String {
            format!("{} {}", self.name, self.age)
        }
    }

    Rust 同一个类可以实现多个特性,每个 impl 块只能实现一个。

    3.2 默认特性

    这是特性与接口的不同点:接口只能规范方法,不能定义方法;但特性可以定义方法作为默认方法。因为是“默认”,所以对象既可以重新定义,也可以不重新定义直接使用默认方法:

    trait Descriptive {
        fn describe(&self) -> String {
            String::from("[Object]")
        }
    }
    struct Person {
        name: String,
        age: u8
    }
    impl Descriptive for Person {
        fn describe(&self) -> String {
            format!("{} {}", self.name, self.age)
        }
    }
    fn main() {
        let cali = Person {
            name: String::from("Cali"),
            age: 24
        };
        println!("{}", cali.describe());
    }
    运行结果:Cali 24

    3.3 特性做参数

    3.3.1 作为入参

    很多情况下需要传递一个函数做参数,比如回调函数、设置按钮事件等。在 Rust 中可以通过传递特性参数来实现:

    fn output(object: impl Descriptive) {
        println!("{}", object.describe());
    }

    任何实现了 Descriptive 特性的对象都可以作为这个函数的参数,函数不需要了解传入对象有没有其他属性或方法,只需要知道它一定有 Descriptive 特性规范的方法。当然,此函数内也无法使用其他属性与方法。

    特性参数还有等效语法:

    fn output(object: T) {
        println!("{}", object.describe());
    }

    如果有多个参数类型均是同一特性:

    fn output_two(arg1: T, arg2: T) {
        println!("{}", arg1.describe());
        println!("{}", arg2.describe());
    }

    特性作类型表示时如果涉及多个特性,可以用 + 符号:

    fn notify(item: impl Summary + Display)
    fn notify(item: T)

    注意:仅用于表示类型的时候,并不意味着可以在 impl 块中使用。

    复杂的实现关系可以使用 where 关键字简化,例如:

    fn some_function(t: T, u: U)
    可以简化成:
    
    fn some_function(t: T, u: U) -> i32 where 
    	T: Display + Clone,
    	U: Clone + Debug

    3.3.2 作为出参

    特性做返回值格式如下:

    fn person() -> impl Descriptive {
        Person {
            name: String::from("Cali"),
            age: 24
        }
    }

    但有一点:特性做返回值只接受实现了该特性的对象做返回值,且在同一个函数中所有可能的返回值类型必须完全一样。比如结构体 A 与结构体 B 都实现了特性 Trait,下面这个函数是错误的:

    fn some_function(bool bl) -> impl Descriptive {
        if bl {
            return A {};
        } else {
            return B {};
        }
    }

    3.4 dyn 关键字

    3.4.1 简介

    dyn 全称 dynamic dispatch(动态分发),dyn Trait 相当于 Java 里的“接口类型引用”——运行期多态。先看 Rust 的两种多态,dyn 是其中一种:

    泛型 dyn Trait
    类比 Java更像 C++ 模板(Java 没有对应物)就是 Java 的接口引用
    分发时机编译期运行期(vtable 查找)
    类型编译期已知,单态化擦除成统一的 trait 对象
    性能零成本,可内联一次间接跳转
    异构集合❌ Vec 里只能放一种 T✅ Vec> 混放圆和方
    方法可否泛型可以❌(object safety 限制)
    // ① 静态分发(泛型):编译期为每个具体类型生成专门代码
    fn draw_static(shape: T) { ... }
    // 编译后相当于生成了 draw_circle、draw_square 两个函数
    // ② 动态分发(dyn):类型被擦除,运行期通过 vtable 找方法
    fn draw_dynamic(shape: &dyn Draw) { ... }
    // 只有一个函数,运行时才查"这个对象到底是哪个类型"

    为什么 dyn 必须装在指针里?因为不同实现类型大小不一样(Circle 4 字节,Square 8 字节),擦除类型后编译器不知道它占多少空间——dyn Trait 是个不定长类型(unsized),不能直接放栈上。其中 & 就是引用,Rust 的引用本质上就是指针,带编译期安全检查的那种。

    所以永远要包一层指针:

    Box      // 堆上,独占
    &dyn Draw           // 借用 = 指针 
    Arc     // 共享,跨线程

    3.4.2 胖指针和vtable

    普通引用是 8 字节(64 位机器),但 &dyn Draw 里类型被擦除了,运行期得知道去哪个类型的方法表查,所以多带一个 vtable 地址,变成 16 字节——这就是“胖”的由来:

    let d: &dyn Draw = &c;
    // d 占 16 字节:[ 0x1000 | 0x7FF6AB00 ]
    //                  │         └──→ vtable(Circle 实现 Draw 的方法表)
    //                  └────────────→ Circle 的数据本体

    vtable 是编译器为某个具体类型 × 某个 trait 的组合生成的静态表,内容如下:

    vtable(Circle as Draw):
    ┌─────────────────────────────────┐
    │ drop_in_place::         │ ← 析构函数指针(Box 丢弃时要调真正的析构)
    │ size_of::   = 4         │ ← 大小(Box 释放堆内存需要知道布局)
    │ align_of::  = 4         │ ← 对齐(同上)
    │ ::draw          │ ← 方法的函数指针
    └─────────────────────────────────┘

    3.4.3 实例分析

    trait Draw {
        fn draw(&self);
    }
    struct Circle {
        r: i32,
    }
    impl Draw for Circle {
        fn draw(&self) {
            println!("画一个半径 {} 的圆", self.r);
        }
    }
    struct Square {
        side: i32,
    }
    impl Draw for Square {
        fn draw(&self) {
            println!("画一个边长 {} 的正方形", self.side);
        }
    }
    // 瘦指针参数:只认 Circle,静态分发
    fn draw_thin(shape: &Circle) {
        shape.draw();
    }
    // 胖指针参数:任何实现了 Draw 的类型都行,动态分发
    fn draw_fat(shape: &dyn Draw) {
        shape.draw();
    }
    #[test]
    fn test_thin_vs_fat() {
        let c = Circle { r: 5 };
        let s = Square { side: 3 };
        // ① 瘦指针:8 字节 = 只有数据地址
        let thin: &Circle = &c;
        assert_eq!(std::mem::size_of::<&Circle>(), 8);
        draw_thin(thin); // 编译期就定死调 ::draw
        // ② 胖指针:16 字节 = 数据地址 + vtable 地址
        let fat: &dyn Draw = &c; // &Circle 自动"增肥"成 &dyn Draw
        assert_eq!(std::mem::size_of::<&dyn Draw>(), 16);
        draw_fat(fat); // 运行期查 vtable 找 draw
        // ③ 胖指针的看家本领:异构集合
        let shapes: Vec<&dyn Draw> = vec![&c, &s]; // 圆和方混装进一个 Vec
        for shape in shapes {
            shape.draw(); // 每个元素运行期各查各的 vtable
        }
    }

    let fat: &dyn Draw = &c; 这行发生了什么?编译器执行了一次“增肥”(unsized coercion):把 &Circle 的瘦指针,拼上 Circle as Draw 的 vtable 地址,组装成 16 字节的胖指针。&s 转 &dyn Draw 时拼的则是 Square 的 vtable——同一个 &dyn Draw 类型,里面装的表不一样,运行时行为自然不同。

    第 ③ 处的 Vec 是 dyn 存在的核心理由:由于 Vec 要求元素类型统一,瘦指针 &Circle 和 &Square 是两个不同类型,混装不了;但擦除成 &dyn Draw 后统一成同一个类型了,随便混。

    本文内容来源于网友投稿,如有侵权请联系删除。
    作者最新文章
    编程开发 Rust
    相关文章 更多
    PHP递归性能优化技巧与迭代替代方案
    PHP递归性能优化技巧与迭代替代方案

    解析PHP递归函数在树形数据处理中的性能瓶颈,提供预加载数据消除I/O、使用显式栈替代深层递归的实战方案,帮助开发者在代码可读性与执行效率间做出合理取舍。

    Java测试中怎么使用Mockito模拟依赖对象
    Java测试中怎么使用Mockito模拟依赖对象

    详细讲解在Java单元测试中如何使用Mockito模拟依赖对象,包括引入依赖、创建Mock、打桩返回值、行为验证以及Mock与Spy的核心差异和常见陷阱排查。

    链表删除节点的时间复杂度是多少及其详细分析
    链表删除节点的时间复杂度是多少及其详细分析

    详细分析链表删除节点的时间复杂度,深入探讨单链表与双向链表在不同已知前提下的查找与删除开销,并结合完整代码与清晰图解进行对比总结。

    codex如何配置模型参数及文件设置教程
    codex如何配置模型参数及文件设置教程

    想知道如何让AI写出的代码更贴合你的习惯?本文手把手教你在VS Code中调整Codex相关模型参数,通过修改配置文件优化温度值和令牌限制,解决代码建议不准确或响应慢的问题。

    Claude Code AI编程工具实力揭秘与编程助手实测
    Claude Code AI编程工具实力揭秘与编程助手实测

    通过实测展示Claude Code在终端中如何理解自然语言指令、自动修改代码文件并处理复杂编程任务,帮助开发者评估其实际辅助能力。

    winforms教程自学入门与基础开发步骤详解
    winforms教程自学入门与基础开发步骤详解

    本教程详细讲解如何使用Visual Studio创建WinForms项目,通过添加按钮和标签控件并编写点击事件代码,实现一个基础的计数器功能,适合C#初学者快速上手Windows窗体应用开发。

    Cursor自动补全设置教程教你快速开启代码补全功能
    Cursor自动补全设置教程教你快速开启代码补全功能

    详解Cursor编辑器中自动补全功能的开启与优化设置,涵盖Tab触发机制、上下文窗口调整及模型切换,帮助开发者解决补全延迟、干扰大等问题,提升编码流畅度。

    pandas的数据格式怎么转换和设置方法教程
    pandas的数据格式怎么转换和设置方法教程

    详解Pandas中数据格式转换的核心方法,包括astype强制转换、to_numeric容错处理及日期解析技巧,解决常见类型错误并提升数据处理效率。

    VS Code中文设置方法 简体语言包安装与切换教程
    VS Code中文设置方法 简体语言包安装与切换教程

    详细介绍在Visual Studio Code中安装Chinese (Simplified)语言包的方法,包括通过扩展市场搜索、安装及自动重启切换至简体中文界面的完整步骤,帮助开发者快速将编辑器本地化。

    cursor安装过程无法更改安装位置的解决方法
    cursor安装过程无法更改安装位置的解决方法

    针对Cursor安装包默认锁定C盘且无路径选择界面的问题,提供通过手动移动文件并创建目录联结(Symbolic Link)的解决方案,实现将软件安装在其他磁盘分区。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    macOS软件
    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    Mac软件 更多
    photoshop
    photoshop
    Windows、macOS 、 iPad

    Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

    Blender
    Blender
    Windows、macOS 和 Linux

    Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。

    灵活计算器
    灵活计算器
    macOS/iOS/Android

    灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

    WINDOWS 更多
    3dmax(3ds max)
    3dmax(3ds max)
    Windows

    Autodesk 3ds Max 是一款专业的三维建模、动画与渲染软件,广泛应用于建筑可视化、游戏开发、影视动画、广告设计和产品展示等领域。

    photoshop
    photoshop
    Windows、macOS 、 iPad

    Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

    Blender
    Blender
    Windows、macOS 和 Linux

    Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。