【Rust类型驱动开发 Type Driven Development】

Rust类型驱动开发


什么是类型驱动开发

type-diven development,即将开发者需要的逻辑约束条件编码到类型系统中,举个例子,一般业务逻辑下开发者通常需要检查输入的正确性,一般情况下开发者的思维惯性会在调用function的内部做一些逻辑判断,比如字符串长度是否符合条件,格式是否符合如人名,邮箱地址等等。类型驱动开发是将此类检查放在形成某一种类型的时候完成,即如果逻辑不通则不会形成需要的类型,同样的,类型驱动开发也具备一定的封装性,保证开发者不会因为误用而破坏流程的安全性和产生一些垃圾数据。


Rust类型驱动开发的实际应用

Rust中类型驱动开发的简单举例

代码示例:

rust 复制代码
use unicode_segmentation::UnicodeSegmentation;

struct Address(String);

impl Address {
    pub fn TryParseEmail(s: String) -> Address {
        let is_empty = s.trim().is_empty();
        let is_too_long = s.graphemes(true).count() > 256;
        let forb_charc = ['/', '(', ')', '"', '<', '>']; //and so on
        let is_legal = s.chars().any(|c| forb_charc.contains(&c));
        if (is_legal || is_empty || is_too_long) {
            panic!("{} is not a valid address", s);
        } else {
            Address(s)
        }
    }
}

fn main() {
    let s: String = "asdfasdf//s".to_string();
    Address::TryParseEmail(s);
}

如上述代码,当程序收集到了一个地址字符串,TryPaseEmail尝试将它转换成一个Address类型,无法形成Address类型的字符串即可标记为不合法的地址,从而抛出错误中断线程。需要注意的是,这里使用的是结构体元组,也就是指定的类型,从而无需在所有需要地址的位置都去判断是否为空,是否合法等操作,这对于大型的程序来说十分友好。

Rust中类型驱动开发的更具体的示例

Rust 中类型驱动开发Api

源代码参考视频地址

此代码实现一个类似进度条的打印,通过trait限定可使用的范围,在编译期间便可抛出更加精准的错误,相较于C++的模板编程,只有在使用到了这段代码的程序才能发现这段代码的问题来讲更加的迅速和及时,而相较于python这种动态语言来讲,python给出的错误一般情况下会摸不着头脑或者既不以预期方式运行也没有报错,这样会使得开发者更加的头疼。

代码示例:

rust 复制代码
use std::{thread::sleep, time::Duration};

struct Unbounded;
struct Bounded {
    bound: usize,
    delims: (char, char),
}
//上述Unbounded,Bounded即描述迭代器是否有边界,既大小是不是"有限的",有限则在实现的角度会添加括号已示边界。
struct Progress<Iter, Bound> {
    iter: Iter,
    i: usize,
    bound: Bound,
}
//Progress,进度条结构体, 包含迭代器,i表示进度条长度,bound表示边界,边界包含长度和用什么符号来表示边界

trait ProgressDisplay: Sized {
    fn display<Iter>(&self, progress: &Progress<Iter, Self>);
}
//trait ProgressDisplay 需要trait Sized以便Progress在编译期大小可知,类型安全,否则无法通过编译
impl ProgressDisplay for Unbounded {
    fn display<Iter>(&self, progress: &Progress<Iter, Self>) {
        println!("{}", "|".repeat(progress.i))
    }
}
impl ProgressDisplay for Bounded {
    fn display<Iter>(&self, progress: &Progress<Iter, Self>) {
        println!(
            "{}{}{}{}",
            progress.bound.delims.0,
            "|".repeat(progress.i),
            " ".repeat(self.bound - progress.i),
            progress.bound.delims.1
        );
    }
}
//分别为有界无界实现打印方式,主要区别,有边界的两边有符号,且长度可知,未遍历到的进度用空格填充
impl<Iter> Progress<Iter, Unbounded> {
    pub fn new(iter: Iter) -> Self {
        Progress {
            iter,
            i: 0,
            bound: Unbounded,
        }
    }
}
//默认无界
impl<Iter, Bound> Iterator for Progress<Iter, Bound>
where
    Iter: Iterator,
    Bound: ProgressDisplay,
{
    type Item = Iter::Item;

    fn next(&mut self) -> Option<Self::Item> {
        self.bound.display(&self);
        self.i += 1;
        self.iter.next()
    }
}
//为进度条实现迭代器,方可迭代,实现关键步骤:Item & next 方法
trait ProgressIteratorExt: Sized {
    fn progress(self) -> Progress<Self, Unbounded>;
}

impl<Iter> ProgressIteratorExt for Iter
where
    Iter: Iterator,
{
    fn progress(self) -> Progress<Self, Unbounded> {
        Progress::new(self)
    }
}
//便于使用Dot(.progress())将容器初始化为进度条
//ExactSizeIterator implementation
impl<Iter> Progress<Iter, Unbounded>
where
    Iter: ExactSizeIterator,
{
    pub fn with_bound(self) -> Progress<Iter, Bounded> {
        let bound = Bounded {
            bound: self.iter.len(),
            delims: ('[', ']'),
        };

        Progress {
            i: self.i,
            iter: self.iter,
            bound,
        }
    }
}
//with bound 会加上默认边界,并将默认的无边界进度条转为有边界的
impl<Iter> Progress<Iter, Bounded> {
    pub fn with_delims(mut self, delims: (char, char)) -> Self {
        self.bound.delims = delims;
        self
    }
}

fn main() {
    let brkt = ('<', '>');
    let vector = vec![1, 2, 3, 4, 5];
    
    for _ in vector.iter().progress().with_bound().with_delims(brkt) {
        sleep(Duration::from_secs(1));
    }
	//以下代码将会出错,错误的调用方法,使用无界progress调用需要有界进度条类型的方法。
    for _ in (0..).progress().with_delims(brkt) {
        sleep(Duration::from_secs(1));
    }
}

以上展示的是在开发者自行实现一些组件的时候,通过类型驱动开发可以进一步保证调用的正确性,常规方式用trait限定和明确的类型定义调用与之相关的方法,从而保证Api的安全稳定。


总结

以上便是Rust中的类型驱动开发的粗浅理解,类型驱动开发并不是一个新概念,而是通过Rust的语言特点可以更好的表达,并且在Rust编程中也很常见,有的开发者称之为新类型模式,这通常体现在,Api的入参验证,通过中间方法,如将string转换成特定的邮箱地址类型,人名类型等等,从而使得开发专注于逻辑本身而不是语言限定本身。

"苟日新,日日新,又日新,常看常新"

相关推荐
彳卸风40 分钟前
Unable to parse timestamp value: “20250220135445“, expected format is
开发语言
bing_1581 小时前
简单工厂模式 (Simple Factory Pattern) 在Spring Boot 中的应用
spring boot·后端·简单工厂模式
dorabighead1 小时前
JavaScript 高级程序设计 读书笔记(第三章)
开发语言·javascript·ecmascript
天上掉下来个程小白1 小时前
案例-14.文件上传-简介
数据库·spring boot·后端·mybatis·状态模式
风与沙的较量丶2 小时前
Java中的局部变量和成员变量在内存中的位置
java·开发语言
水煮庄周鱼鱼2 小时前
C# 入门简介
开发语言·c#
Asthenia04122 小时前
基于Jackson注解的JSON工具封装与Redis集成实战
后端
编程星空2 小时前
css主题色修改后会多出一个css吗?css怎么定义变量?
开发语言·后端·rust
软件黑马王子2 小时前
Unity游戏制作中的C#基础(6)方法和类的知识点深度剖析
开发语言·游戏·unity·c#
Logintern093 小时前
使用VS Code进行Python编程的一些快捷方式
开发语言·python