rust学习二十.10、RUST高级类型之新类型模式和类型别名

这两个内容都比较容易理解。

一、新类型(newtype)模式

注意,这里说的是一种编程模式,不是说有一个叫newtype的类型。

这种编程模式的含义:为某个类型作个封装,构建一个新的类型,以便绕过某些束缚,从而达成特定目的。

目的如下:

a.用于抽象掉一些类型的实现细节

b.可以隐藏其内部的泛型类型

c.实现曲线救国

一个典型的例子,利用新类型绕过孤儿规则。

二、类型别名

语法

type  xxx=现有的类型

看起来有点像特质中的关联类型定义。

附:特质关联类型实现示例

impl Fight for Person {
    type Item = Animal;
    fn attack(&self, other: &self::Item) {
        println!(
            "{}岁{}(性别:{}) 攻击了 {}岁{}",
            self.age,
            self.name,
            self.sex,
            other.age,
            other.name
        );
    }
    fn defend<T: Danger>(&self, danger: &T) {
        println!("{},{}岁{}(性别:{}) 奋起并力图战胜它们",danger.happen(), self.age, self.name, self.sex);
    }
}

要点

a.简化类型书写
b.使得代码更容易阅读
c.也更方便维护,例如如果许多方法都要求用同个类型,那么这样定义之后就不容易搞错,
  作用上和java中定义的枚举常量一个道理。

在rust的标准库中,有不少的类型别名,例如Result<T,E>等。

定义说明

a.可以为别名定义别名

b.没有明确的别名层级限定,意思就是你定义了别名a,然后基于a定义b,依次类推,可以定义type n=m..  .有多少层级限定不知道,不过定义个2次应该没有问题的。

 

rust的类型别名是一种类似Microsoft SqlServer自定义类型的东西。

在 SQL Server 中,可以通过 用户定义数据类型(User-Defined Data Types, UDT) 创建自定义类型,本质上是基于系统数据类型的别名,并附加约束或默认值。

例如:

-- 创建类型:限制长度并校验格式
CREATE TYPE dbo.Email 
FROM VARCHAR(255) 
NOT NULL 
WITH CHECK (CHARINDEX('@', value) > 0);

-- 使用类型
CREATE TABLE Users (
    UserID INT PRIMARY KEY,
    UserEmail dbo.Email
);

-- 测试插入
INSERT INTO Users VALUES (1, 'test@example.com');  -- 成功
INSERT INTO Users VALUES (2, 'invalid-email');     -- 失败,触发 CHECK 约束

只要做过数据库设计,就知道这个东西多好用! 好记、容易保证相同的属性使用相同的类型、维护也非常容易。

如果修改了type,就会发现有的时候,这真是一个绝妙的注意。

这种好东西在postgresql和mysql(8以上)都有类似的实现。

 

所以,毫无疑问,在rust中这是一个绝妙主意,因为我们都知道rust的类型之复杂丑陋是出名的,有的时候让人心烦,又会耗费很多时间打字,而又了别名就会在某些情况下,大大改善这个现象。

 

三、新类型示例

use student::*;
use teacher::*;

fn main() {
    let lml = Studentinfo {
        name: String::from("lml"),
        age: 18,
        gender: String::from("女"),
        no: String::from("12101"),
    };
    print_student(&lml);
    lml.learn();
    lml.sleep();

    let lu = Teacherinfo {
        name: String::from("lu"),
        age: 46,
        gender: String::from("男"),
        position: String::from("教研组长"),
    };
    lu.teach_student(&lml);
    print_teacher(&lu);
    let _me = Box::new(String::from("中国"));

    let my_lu = MyTeacherinfo(lu);
    my_lu.print();
}

struct MyTeacherinfo(Teacherinfo);
impl Print for MyTeacherinfo {
    fn print(&self) {
        println!(
            "教师基本信息-姓名:{},年龄:{},性别:{},职位:{}",
            self.0.name, self.0.age, self.0.gender, self.0.position
        );
    }
}

本例中,Teacherinfo是外部包类型。

四、类型别名示例

struct Person {
    name: String,
    age: u8,
}
type Teacher = Person;
type Student = Person;
type GoodPeople = Student;
type Thunk = Box<dyn Fn() + Send + 'static>;

fn print_student(p: &Student) {
    println!("name={},age={}", p.name, p.age);
}

fn print_me(p: &GoodPeople) {
    println!("name={},age={}", p.name, p.age);
}

fn main() {
    let f: Thunk = Box::new(|| println!("hi"));
    //应为Thunk是Box类型,已经实现了Deref特质,它可以自动解引用,所以可以直接调用。
    f();
    //如果想显式调用,可以这样写:
    (*f)();
    let p = Student {
        name: "张三".to_string(),
        age: 30,
    };
    print_student(&p);

    let g = GoodPeople {
        name: "lzf".to_string(),
        age: 99,
    };
    print_me(&g);
}
#[allow(unused)]
fn takes_long_type(f: Thunk) {
    // --snip--
}
#[allow(unused)]
fn returns_long_type() -> Thunk {
    // --snip--
    Box::new(|| {})
}

上例中,Student是Person别名,GoodPerson是Student的别名。

运行结果:

五、小结

1.类型别名的确是一个很好的东西

2.只要写过代码,就知道它能让代码更简单、易读、易维护

 

posted @ 2025-04-03 19:31  正在战斗中  阅读(72)  评论(0)    收藏  举报