【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转换成特定的邮箱地址类型,人名类型等等,从而使得开发专注于逻辑本身而不是语言限定本身。

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

相关推荐
我是哈哈hh7 分钟前
HTML5和CSS3的进阶_HTML5和CSS3的新增特性
开发语言·前端·css·html·css3·html5·web
Dontla41 分钟前
Rust泛型系统类型推导原理(Rust类型推导、泛型类型推导、泛型推导)为什么在某些情况必须手动添加泛型特征约束?(泛型trait约束)
开发语言·算法·rust
梦想画家43 分钟前
精通rust宏系列教程-入门篇
rust·元编程·rust宏
喜欢打篮球的普通人1 小时前
rust模式和匹配
java·算法·rust
java小吕布1 小时前
Java中的排序算法:探索与比较
java·后端·算法·排序算法
Neophyte06082 小时前
C++算法练习-day40——617.合并二叉树
开发语言·c++·算法
Goboy2 小时前
工欲善其事,必先利其器;小白入门Hadoop必备过程
后端·程序员
慕容复之巅2 小时前
基于MATLAB的条形码的识别图像处理报告
开发语言·图像处理·matlab
zqzgng2 小时前
Python 数据可视化pilot
开发语言·python·信息可视化
写bug的小屁孩2 小时前
websocket初始化
服务器·开发语言·网络·c++·websocket·网络协议·qt creator