泛型约束:where vs 特性约束 (Trait Bounds)
你将学到: Rust 的特性约束与 C# 的
where约束对比;where子句语法;条件化特性实现;关联类型;以及高阶特性约束 (HRTBs)。难度: 🔴 高级
C# 泛型约束
// 使用 where 子句的 C# 泛型约束
public class Repository<T> where T : class, IEntity, new()
{
public T Create()
{
return new T(); // new() 约束允许调用无参构造函数
}
public void Save(T entity)
{
if (entity.Id == 0) // IEntity 约束提供了 Id 属性
{
entity.Id = GenerateId();
}
// 保存到数据库
}
}
// 带有不同约束的多个类型参数
public class Converter<TInput, TOutput>
where TInput : IConvertible
where TOutput : class, new()
{
public TOutput Convert(TInput input)
{
var output = new TOutput();
// 使用 IConvertible 进行转换逻辑
return output;
}
}
// 泛型中的变体 (Variance)
public interface IRepository<out T> where T : IEntity
{
IEnumerable<T> GetAll(); // 协变 (Covariant) —— 可以返回派生程度更高的类型
}
public interface IWriter<in T> where T : IEntity
{
void Write(T entity); // 逆变 (Contravariant) —— 可以接受派生程度更低的基类类型
}
带有特性约束的 Rust 泛型
#![allow(unused)]
fn main() {
use std::fmt::{Debug, Display};
use std::clone::Clone;
// 基础特性约束
pub struct Repository<T>
where
T: Clone + Debug + Default,
{
items: Vec<T>,
}
impl<T> Repository<T>
where
T: Clone + Debug + Default,
{
pub fn new() -> Self {
Repository { items: Vec::new() }
}
pub fn create(&self) -> T {
T::default() // Default 特性提供默认值
}
pub fn add(&mut self, item: T) {
println!("正在添加项:{:?}", item); // Debug 特性用于打印输出
self.items.push(item);
}
pub fn get_all(&self) -> Vec<T> {
self.items.clone() // Clone 特性用于复制数据
}
}
// 带有不同语法的多重特性约束
pub fn process_data<T, U>(input: T) -> U
where
T: Display + Clone,
U: From<T> + Debug,
{
println!("正在处理:{}", input); // Display 特性
let cloned = input.clone(); // Clone 特性
let output = U::from(cloned); // From 特性用于转换
println!("结果:{:?}", output); // Debug 特性
output
}
// 关联类型 (类似于 C# 的泛型参数约束)
pub trait Iterator {
type Item; // 关联类型,而非显式的泛型参数
fn next(&mut self) -> Option<Self::Item>;
}
pub trait Collect<T> {
fn collect<I: Iterator<Item = T>>(iter: I) -> Self;
}
// 高阶特性约束 (Higher-ranked trait bounds - 进阶内容)
fn apply_to_all<F>(items: &[String], f: F) -> Vec<String>
where
F: for<'a> Fn(&'a str) -> String, // 该函数适用于任何生命周期
{
items.iter().map(|s| f(s)).collect()
}
// 条件化特性实现
impl<T> PartialEq for Repository<T>
where
T: PartialEq + Clone + Debug + Default,
{
fn eq(&self, other: &Self) -> bool {
self.items == other.items
}
}
}
graph TD
subgraph "C# 泛型约束"
CS_WHERE["where T : class, IInterface, new()"]
CS_RUNTIME["[错误] 部分运行时类型检查<br/>虚方法分发"]
CS_VARIANCE["[OK] 协变/逆变<br/>in/out 关键字"]
CS_REFLECTION["[错误] 可能通过反射进行运行时操作<br/>typeof(T), is, as 运算符"]
CS_BOXING["[错误] 值类型在接口约束下<br/>会发生装箱 (Boxing)"]
CS_WHERE --> CS_RUNTIME
CS_WHERE --> CS_VARIANCE
CS_WHERE --> CS_REFLECTION
CS_WHERE --> CS_BOXING
end
subgraph "Rust 特性约束"
RUST_WHERE["where T: Trait + Clone + Debug"]
RUST_COMPILE["[OK] 编译时决议<br/>单态化 (Monomorphization)"]
RUST_ZERO["[OK] 零成本抽象<br/>无运行时开销"]
RUST_ASSOCIATED["[OK] 关联类型<br/>比泛型更灵活"]
RUST_HKT["[OK] 高阶特性约束<br/>更进阶的类型关系"]
RUST_WHERE --> RUST_COMPILE
RUST_WHERE --> RUST_ZERO
RUST_WHERE --> RUST_ASSOCIATED
RUST_WHERE --> RUST_HKT
end
subgraph "灵活性对比"
CS_FLEX["C# 的灵活性<br/>[OK] 变体支持<br/>[OK] 运行时类型信息<br/>[错误] 性能损耗"]
RUST_FLEX["Rust 的灵活性<br/>[OK] 零成本<br/>[OK] 编译时安全<br/>[错误] 目前暂不支持变体"]
end
style CS_RUNTIME fill:#fff3e0,color:#000
style CS_BOXING fill:#ffcdd2,color:#000
style RUST_COMPILE fill:#c8e6c9,color:#000
style RUST_ZERO fill:#c8e6c9,color:#000
style CS_FLEX fill:#e3f2fd,color:#000
style RUST_FLEX fill:#c8e6c9,color:#000
练习
🏋️ 练习:泛型仓储 (Generic Repository) (点击展开)
将以下 C# 泛型仓储接口翻译为 Rust 的特性:
public interface IRepository<T> where T : IEntity, new()
{
T GetById(int id);
IEnumerable<T> Find(Func<T, bool> predicate);
void Save(T entity);
}
要求:
- 定义一个
Entity特性,包含fn id(&self) -> u64。 - 定义一个
Repository<T>特性,其中T: Entity + Clone。 - 实现一个
InMemoryRepository<T>,使用Vec<T>存储数据。 find方法应接受impl Fn(&T) -> bool。
🔑 参考答案
trait Entity: Clone {
fn id(&self) -> u64;
}
trait Repository<T: Entity> {
fn get_by_id(&self, id: u64) -> Option<&T>;
fn find(&self, predicate: impl Fn(&T) -> bool) -> Vec<&T>;
fn save(&mut self, entity: T);
}
struct InMemoryRepository<T> {
items: Vec<T>,
}
impl<T: Entity> InMemoryRepository<T> {
fn new() -> Self { Self { items: Vec::new() } }
}
impl<T: Entity> Repository<T> for InMemoryRepository<T> {
fn get_by_id(&self, id: u64) -> Option<&T> {
self.items.iter().find(|item| item.id() == id)
}
fn find(&self, predicate: impl Fn(&T) -> bool) -> Vec<&T> {
self.items.iter().filter(|item| predicate(item)).collect()
}
fn save(&mut self, entity: T) {
if let Some(pos) = self.items.iter().position(|e| e.id() == entity.id()) {
self.items[pos] = entity;
} else {
self.items.push(entity);
}
}
}
#[derive(Clone, Debug)]
struct User { user_id: u64, name: String }
impl Entity for User {
fn id(&self) -> u64 { self.user_id }
}
fn main() {
let mut repo = InMemoryRepository::new();
repo.save(User { user_id: 1, name: "Alice".into() });
repo.save(User { user_id: 2, name: "Bob".into() });
let found = repo.find(|u| u.name.starts_with('A'));
assert_eq!(found.len(), 1);
}
与 C# 的关键区别:没有 new() 约束(使用 Default 特性替代)。使用 Fn(&T) -> bool 替代 Func<T, bool>。返回 Option 而非抛出异常。