当前位置: 首页 > news >正文

Rust 练习册 :Luhn Trait与Trait实现

在Rust中,trait是定义共享行为的主要方式,类似于其他语言中的接口。通过trait,我们可以为不同类型实现相同的方法,实现代码的复用和抽象。在 Exercism 的 “luhn-trait” 练习中,我们需要为Luhn trait实现多种类型的valid_luhn方法。这不仅能帮助我们掌握Rust的trait系统,还能深入学习如何设计灵活且可扩展的API。

什么是Trait?

Trait是Rust中定义共享行为的方式,它声明了某些类型必须提供的方法集合。Trait类似于其他语言中的接口,但更加强大和灵活。通过实现trait,类型可以共享相同的行为,这使得我们可以编写更加通用和可复用的代码。

在我们的练习中,需要为Luhn trait实现valid_luhn方法,使其能够为多种类型(如&str、String、各种整数类型)提供Luhn校验和验证功能。

让我们先看看练习提供的trait定义和示例实现:

pub trait Luhn {fn valid_luhn(&self) -> bool;
}/// Here is the example of how to implement custom Luhn trait
/// for the &str type. Naturally, you can implement this trait
/// by hand for the every other type presented in the test suite,
/// but your solution will fail if a new type is presented.
/// Perhaps there exists a better solution for this problem?
impl<'a> Luhn for &'a str {fn valid_luhn(&self) -> bool {unimplemented!("Determine if '{}' is a valid credit card number.", self);}
}

我们需要为Luhn trait实现valid_luhn方法,使其能够为多种类型提供Luhn校验功能。

设计分析

1. 核心要求

  1. Trait定义:定义Luhn trait及valid_luhn方法
  2. 类型实现:为多种类型实现Luhn trait
  3. 算法实现:实现Luhn校验和验证算法
  4. API设计:设计简洁且易于使用的API

2. 技术要点

  1. 泛型实现:使用泛型和trait约束减少重复代码
  2. 字符串处理:处理各种类型的输入数据
  3. 生命周期管理:正确处理引用的生命周期
  4. 代码复用:避免为每种类型重复实现相同逻辑

完整实现

1. 基础实现

pub trait Luhn {fn valid_luhn(&self) -> bool;
}impl Luhn for str {fn valid_luhn(&self) -> bool {// 移除空格并验证字符let cleaned: String = self.chars().filter(|c| !c.is_whitespace()).collect();// 检查长度和字符有效性if cleaned.len() <= 1 || !cleaned.chars().all(|c| c.is_ascii_digit()) {return false;}let digits: Vec<u32> = cleaned.chars().map(|c| c.to_digit(10).unwrap()).collect();let mut sum = 0;let len = digits.len();for (i, &digit) in digits.iter().enumerate() {// 从右到左,偶数位置的数字需要乘以2if (len - i) % 2 == 0 {let doubled = digit * 2;sum += if doubled > 9 { doubled - 9 } else { doubled };} else {sum += digit;}}sum % 10 == 0}
}impl Luhn for String {fn valid_luhn(&self) -> bool {self.as_str().valid_luhn()}
}impl Luhn for u8 {fn valid_luhn(&self) -> bool {self.to_string().valid_luhn()}
}impl Luhn for u16 {fn valid_luhn(&self) -> bool {self.to_string().valid_luhn()}
}impl Luhn for u32 {fn valid_luhn(&self) -> bool {self.to_string().valid_luhn()}
}impl Luhn for u64 {fn valid_luhn(&self) -> bool {self.to_string().valid_luhn()}
}impl Luhn for usize {fn valid_luhn(&self) -> bool {self.to_string().valid_luhn()}
}

2. 使用泛型优化的实现

pub trait Luhn {fn valid_luhn(&self) -> bool;
}// 为str实现Luhn trait
impl Luhn for str {fn valid_luhn(&self) -> bool {let digits: Vec<u32> = self.chars().filter(|c| !c.is_whitespace()).map(|c| c.to_digit(10)).collect::<Option<Vec<u32>>>().unwrap_or_default();// 必须至少有2位数字if digits.len() <= 1 {return false;}let sum: u32 = digits.iter().rev() // 从右到左处理.enumerate().map(|(i, &digit)| {if i % 2 == 1 { // 偶数位置(从右数第2、4、6...位)let doubled = digit * 2;if doubled > 9 { doubled - 9 } else { doubled }} else {digit}}).sum();sum % 10 == 0}
}// 为String实现Luhn trait
impl Luhn for String {fn valid_luhn(&self) -> bool {self.as_str().valid_luhn()}
}// 为数字类型实现Luhn trait
impl Luhn for u8 {fn valid_luhn(&self) -> bool {self.to_string().valid_luhn()}
}impl Luhn for u16 {fn valid_luhn(&self) -> bool {self.to_string().valid_luhn()}
}impl Luhn for u32 {fn valid_luhn(&self) -> bool {self.to_string().valid_luhn()}
}impl Luhn for u64 {fn valid_luhn(&self) -> bool {self.to_string().valid_luhn()}
}impl Luhn for usize {fn valid_luhn(&self) -> bool {self.to_string().valid_luhn()}
}

3. 使用宏减少重复代码的实现

pub trait Luhn {fn valid_luhn(&self) -> bool;
}impl Luhn for str {fn valid_luhn(&self) -> bool {let digits: Vec<u32> = self.chars().filter(|c| !c.is_whitespace()).map(|c| c.to_digit(10)).collect::<Option<Vec<u32>>>().unwrap_or_default();if digits.len() <= 1 {return false;}let sum: u32 = digits.iter().rev().enumerate().map(|(i, &digit)| {if i % 2 == 1 {let doubled = digit * 2;if doubled > 9 { doubled - 9 } else { doubled }} else {digit}}).sum();sum % 10 == 0}
}impl Luhn for String {fn valid_luhn(&self) -> bool {self.as_str().valid_luhn()}
}// 使用宏为数字类型实现Luhn trait
macro_rules! impl_luhn_for_numbers {($($t:ty),*) => {$(impl Luhn for $t {fn valid_luhn(&self) -> bool {self.to_string().valid_luhn()}})*};
}impl_luhn_for_numbers!(u8, u16, u32, u64, usize);

测试用例分析

通过查看测试用例,我们可以更好地理解需求:

#[test]
fn you_can_validate_from_a_str() {assert!("046 454 286".valid_luhn());assert!(!"046 454 287".valid_luhn());
}

可以直接在&str上调用valid_luhn方法进行验证。

#[test]
fn you_can_validate_from_a_string() {assert!(String::from("046 454 286").valid_luhn());assert!(!String::from("046 454 287").valid_luhn());
}

可以在String上调用valid_luhn方法进行验证。

#[test]
fn you_can_validate_from_a_u8() {assert!(240u8.valid_luhn());assert!(!241u8.valid_luhn());
}

可以在u8上调用valid_luhn方法进行验证。

#[test]
fn you_can_validate_from_a_u16() {let valid = 64_436u16;let invalid = 64_437u16;assert!(valid.valid_luhn());assert!(!invalid.valid_luhn());
}

可以在u16上调用valid_luhn方法进行验证。

性能优化版本

考虑性能的优化实现:

pub trait Luhn {fn valid_luhn(&self) -> bool;
}impl Luhn for str {fn valid_luhn(&self) -> bool {let mut digits = Vec::with_capacity(self.len());// 预分配并验证字符for c in self.chars() {match c {' ' => continue, // 跳过空格'0'..='9' => digits.push(c as u32 - '0' as u32),_ => return false, // 非数字字符}}// 至少需要2位数字if digits.len() <= 1 {return false;}let mut sum = 0u32;// 从右到左处理,使用索引计算位置for (i, &digit) in digits.iter().rev().enumerate() {if i % 2 == 1 { // 偶数位置(从右数第2、4、6...位)let doubled = digit * 2;sum += if doubled > 9 { doubled - 9 } else { doubled };} else {sum += digit;}}sum % 10 == 0}
}impl Luhn for String {fn valid_luhn(&self) -> bool {self.as_str().valid_luhn()}
}// 使用宏为数字类型实现Luhn trait
macro_rules! impl_luhn_for_numbers {($($t:ty),*) => {$(impl Luhn for $t {fn valid_luhn(&self) -> bool {self.to_string().valid_luhn()}})*};
}impl_luhn_for_numbers!(u8, u16, u32, u64, usize);

错误处理和边界情况

考虑更多边界情况的实现:

pub trait Luhn {fn valid_luhn(&self) -> bool;
}#[derive(Debug, PartialEq)]
pub enum LuhnError {TooShort,InvalidCharacter(char),EmptyInput,
}impl Luhn for str {fn valid_luhn(&self) -> bool {self.validate_luhn().unwrap_or(false)}
}impl str {pub fn validate_luhn(&self) -> Result<bool, LuhnError> {if self.is_empty() {return Err(LuhnError::EmptyInput);}let mut digits = Vec::new();for c in self.chars() {if c.is_whitespace() {continue;}match c.to_digit(10) {Some(digit) => digits.push(digit),None => return Err(LuhnError::InvalidCharacter(c)),}}if digits.len() <= 1 {return Err(LuhnError::TooShort);}let sum: u32 = digits.iter().rev().enumerate().map(|(i, &digit)| {if i % 2 == 1 {let doubled = digit * 2;if doubled > 9 { doubled - 9 } else { doubled }} else {digit}}).sum();Ok(sum % 10 == 0)}
}impl Luhn for String {fn valid_luhn(&self) -> bool {self.as_str().valid_luhn()}
}// 使用宏为数字类型实现Luhn trait
macro_rules! impl_luhn_for_numbers {($($t:ty),*) => {$(impl Luhn for $t {fn valid_luhn(&self) -> bool {self.to_string().valid_luhn()}})*};
}impl_luhn_for_numbers!(u8, u16, u32, u64, usize);

扩展功能

基于基础实现,我们可以添加更多功能:

pub trait Luhn {fn valid_luhn(&self) -> bool;// 扩展方法:获取清理后的数字fn clean_digits(&self) -> Result<Vec<u32>, LuhnError>whereSelf: Sized,{let s = self.to_string();s.as_str().clean_digits()}// 扩展方法:生成校验位fn generate_check_digit(&self) -> Option<u32>whereSelf: Sized,{let s = self.to_string();s.as_str().generate_check_digit()}
}pub trait LuhnExt: Luhn {fn clean_digits(&self) -> Result<Vec<u32>, LuhnError>;fn generate_check_digit(&self) -> Option<u32>;
}impl Luhn for str {fn valid_luhn(&self) -> bool {self.validate_luhn().unwrap_or(false)}
}impl LuhnExt for str {fn clean_digits(&self) -> Result<Vec<u32>, LuhnError> {if self.is_empty() {return Err(LuhnError::EmptyInput);}let mut digits = Vec::new();for c in self.chars() {if c.is_whitespace() {continue;}match c.to_digit(10) {Some(digit) => digits.push(digit),None => return Err(LuhnError::InvalidCharacter(c)),}}Ok(digits)}fn generate_check_digit(&self) -> Option<u32> {let digits = self.clean_digits().ok()?;// 添加一个占位符作为校验位let mut extended_digits = digits.clone();extended_digits.push(0);let sum: u32 = extended_digits.iter().rev().enumerate().map(|(i, &digit)| {if i % 2 == 1 {let doubled = digit * 2;if doubled > 9 { doubled - 9 } else { doubled }} else {digit}}).sum();let check_digit = (10 - (sum % 10)) % 10;Some(check_digit)}
}impl str {pub fn validate_luhn(&self) -> Result<bool, LuhnError> {if self.is_empty() {return Err(LuhnError::EmptyInput);}let mut digits = Vec::new();for c in self.chars() {if c.is_whitespace() {continue;}match c.to_digit(10) {Some(digit) => digits.push(digit),None => return Err(LuhnError::InvalidCharacter(c)),}}if digits.len() <= 1 {return Err(LuhnError::TooShort);}let sum: u32 = digits.iter().rev().enumerate().map(|(i, &digit)| {if i % 2 == 1 {let doubled = digit * 2;if doubled > 9 { doubled - 9 } else { doubled }} else {digit}}).sum();Ok(sum % 10 == 0)}
}impl Luhn for String {fn valid_luhn(&self) -> bool {self.as_str().valid_luhn()}
}// 使用宏为数字类型实现Luhn trait
macro_rules! impl_luhn_for_numbers {($($t:ty),*) => {$(impl Luhn for $t {fn valid_luhn(&self) -> bool {self.to_string().valid_luhn()}})*};
}impl_luhn_for_numbers!(u8, u16, u32, u64, usize);#[derive(Debug, PartialEq)]
pub enum LuhnError {TooShort,InvalidCharacter(char),EmptyInput,
}

实际应用场景

Trait和Luhn算法在实际开发中有以下应用:

  1. 支付系统:验证信用卡号和借记卡号
  2. API设计:创建灵活且易于使用的API
  3. 数据验证:验证各种识别号码的准确性
  4. 表单处理:处理用户输入的不同数据类型
  5. 库开发:设计可扩展的库接口
  6. 类型安全:确保类型转换的安全性
  7. 移动设备:验证IMEI号码
  8. 身份验证:验证各种身份证件号码

算法复杂度分析

  1. 时间复杂度:O(n)

    • 需要遍历字符串中的每个字符,其中n是字符串长度
  2. 空间复杂度:O(n)

    • 需要存储过滤后的数字数组

与其他实现方式的比较

// 使用默认实现的trait
pub trait Luhn {fn valid_luhn(&self) -> bool {let s = self.to_string();// Luhn算法实现let digits: Vec<u32> = s.chars().filter(|c| !c.is_whitespace()).map(|c| c.to_digit(10)).collect::<Option<Vec<u32>>>().unwrap_or_default();if digits.len() <= 1 {return false;}let sum: u32 = digits.iter().rev().enumerate().map(|(i, &digit)| {if i % 2 == 1 {let doubled = digit * 2;if doubled > 9 { doubled - 9 } else { doubled }} else {digit}}).sum();sum % 10 == 0}
}impl Luhn for str {}
impl Luhn for String {}
impl Luhn for u8 {}
impl Luhn for u16 {}
impl Luhn for u32 {}
impl Luhn for u64 {}
impl Luhn for usize {}// 使用关联类型的trait
pub trait Luhn {type Error;fn valid_luhn(&self) -> Result<bool, Self::Error>;
}impl Luhn for str {type Error = LuhnError;fn valid_luhn(&self) -> Result<bool, Self::Error> {// 实现...Ok(true)}
}// 使用泛型trait
pub trait Luhn<T> {fn valid_luhn(&self) -> bool;
}impl Luhn<&str> for &str {fn valid_luhn(&self) -> bool {// 实现...true}
}// 使用继承trait
pub trait Validate {fn validate(&self) -> bool;
}pub trait Luhn: Validate {fn valid_luhn(&self) -> bool {self.validate()}
}impl Validate for str {fn validate(&self) -> bool {// 实现...true}
}impl Luhn for str {}

总结

通过 luhn-trait 练习,我们学到了:

  1. Trait系统:掌握了Rust中trait的定义和实现
  2. 泛型编程:学会了使用泛型减少重复代码
  3. 宏系统:了解了如何使用宏简化重复实现
  4. API设计:学会了设计灵活且易于使用的API
  5. 类型系统:深入理解了Rust的类型系统和trait系统
  6. 代码复用:学会了如何避免重复代码并提高可维护性

这些技能在实际开发中非常有用,特别是在设计库API、处理类型转换、构建可扩展系统等场景中。Trait虽然是Rust类型系统的核心概念,但它体现了Rust设计哲学中组合优于继承的思想,是学习Rust高级特性的良好起点。

通过这个练习,我们也看到了Rust在trait系统和API设计方面的强大能力,以及如何用安全且灵活的方式实现可扩展的类型系统。这种结合了安全性和灵活性的语言特性正是Rust的魅力所在。

http://www.dtcms.com/a/581722.html

相关文章:

  • 家庭机器人,从科幻到日常的二十年突围战
  • 网站html地图导航代码大全网站功能的介绍
  • Android开发(Kotlin) 高阶函数、内联函数
  • AI安全与网络安全的融合:从挑战到解决方案
  • 从零开始构建现代化React应用:最佳实践与性能优化
  • 国外的网站建设公司广州工商注册服务中心
  • 【tips】常用不同状态小圆点样式css
  • 保险微网站制作公司网站费用计入什么科目
  • SSM网上水果商城s7436(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 北京市建设信息网站湖南手机版建站系统信息
  • 【函数参数传递方式选择指南(C/C++)】
  • 做ppt的图片素材网站数字营销成功案例
  • 企业网站子页面模板网站 开发 外包
  • 机器学习日报14
  • 解决Mac不能识别#include <bits/stdc++.h> 头文件问题
  • 基于站点数据进行遥感机器学习参数反演-以XGBOOST反演LST为例(附带数据与代码)试读
  • 四面山网站建设现在帮别人做网站赚钱不
  • 破解EEG逆问题:ADMM-ESINet如何融合优化理论与深度学习实现实时源成像
  • CSS 高中低部分面试题方法及知识点介绍
  • GMI Cloud@AI周报 | Cursor 2.0发布自研模型Composer;小鹏发布新一代人形机器人 IRON
  • 莱芜手机网站建设报价网站建设平台策划
  • 【jmeter】-安装-插件安装
  • 猫头虎AI分享:CodeBuddy IDE 已支持 GLM-4.6!亲测更强了
  • 云手机能够流畅运行大型游戏吗
  • 【App开发】手机投屏的几种方式(含QtScrcpy)- Android 开发新人指南
  • 云手机 一梦江湖畅玩搬砖
  • 智享账单管理利器:Rachoon
  • 惠州网站小程序建设点网站制作的评价标准
  • Ascend C流与任务管理实战:构建高效的异步计算管道
  • 阶段性总结