rust学习二十.5、RUST特质中的关联类型
这是rust特质(trait)中颇有意思的一个特性。一、前言
这个特性,在前面已经有接触过,例如书本的第十三章节的迭代器特质就有这个例子:
impl Iterator for BooksIterator {
type Item = String;
fn next(&mut self) -> Option<String> {
if self.current_index < 3 {
let item = self.strs.clone();
self.current_index += 1;
Some(item)
} else {
None
}
}
}初看这个代码,颇为迷惑,为什么要这么搞,难道用通用类型不好吗?
现在知道了,这个是rust特质的关联类型(associated type)。
很自然地有个想法,关联类型有什么用?为什么不能使用通用类型?
根据书本的示例,我自行体验了一番,总结出一点:
由于rust版本的缘故,使用关联类型更加灵活,或者说源于rust目前版本通用类型的局限性,rust的关联类型是通用类型的重要补充。
二、rust为什么要使用关联类型?
大体上可以这么说,这是rust此类语言的局限性所导致的。此类语言主张废弃继承,认为继承的坏处多于好处(当然应该是和它们的设计目标有关),比较典型的有rust和go。
然而废弃对象的继承也有很多的副作用,主要是工程上的,非性能和安全上的。
现在开发业务系统的后台语言java中,因为有了继承,它的通用类型就能够以不同于rust的方式进行使用。
举个例子:
public abstract class ExpParser<T extends ParamData> {
}
public class SpelParser extends ExpParser<SpelExp>{
}
public interface Study<T>{
public void learn(T t);
public void gather(T t);
}
public class Student implements Study<T>{
public void lean(T t){
if (t instanceof ChinaStudent){
}
else if (t instanceof AmericaStudent){
}
else{
}
}
}这里,Java就可以方便地限定T的类型,T是ParamData的子类。此外,即使不限定T的类型,也可以简单地通过 instanceof 语法来操作。
但在rust中无法这么进行限定(通过继承指定范围),所以rust中如果要限定对象类型范围,那么就通过where语句或者关联类型来进行。
由于rust的特性,它基本上需要在编译的时候需要知道实现类或者方法的具体类型,所以某种程度上,不如具有继承特性的语言来得方便。
但rust这种关联特性也有个好处:可以在实现类/方法中指定关联类型的具体类型,从而增强了灵活性。
所以,rust通过关联类型主要解决1个问题:
可以消除通用类型的局限性。rust的通用类型在方法中必须罗列各种可能的类型,如果方法有许多实现,这就非常不方便了,但是用了关联对象就可以避免,具体是在实现对象中列出
这种方式,看起来有点像设计模式的工厂模式。
例如有以下一个比较奇怪的特质:
trait Danger{
fn happen(&self,t:T)
where T:Display+Clone+Go+Run+Stop+Walk+Fly;
}这个是不是很古怪?
但是用了关联类型,where语句中的内容就可以分散到具体实现对象中。
下文的例子可以说明这个问题。
定义和使用关联类型
1.在特质内使用type关键字定义个关联类型(占位符)
2.在具体的实现方法中,把占位符替换为实际类型
例如:
trait Fight {
type Item;
fn attack(&self, other: &Self::Item);
fn defend<T>(&self, danger: &T)
where T: Danger;
}
impl Fight for Person {
type Item = Animal;
//其余略
}在特质Fight中Item称为关联类型占位符,在具体的结构体Person中,不许把占位符替换为具体的类型(这里是Animal)。
就是这么简单!
三、示例
trait Danger {
fn happen(&self)->String;
}
struct Fire{
address: String
}
impl Danger for Fire{
fn happen(&self)->String {
//返回address+"燃烧了"
self.address.clone()+"燃烧了"
}
}
trait Fight {
type Item;
fn attack(&self, other: &Self::Item);
fn defend<T>(&self, danger: &T)
where T: Danger;
}
#
struct Person {
name: String,
age: u32,
sex: String,
}
#
struct Animal {
name: String,
age: u32,
}
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);
}
}
impl Fight for Animal {
type Item = Person;
fn attack(&self, other: &Self::Item) {
println!("{}岁{} 攻击了 {}岁{}", self.age, self.name, other.age, other.name);
}
fn defend<T: Danger>(&self, danger: &T) {
println!("{},{}岁{} 跑了", danger.happen(),self.age, self.name);
}
}
fn draw_fire() {
println!(" (. )");
println!(" ) ( )");
println!(" .' . '.'.");
println!(" ( , ) (. )( ', )");
println!(" .' ) ( . ) ,( , ) ( .");
println!("). , ( . () ( , ').' (, )");
println!("(_,) . ), ) _) _,')(, ) '. ),. (' )");
println!("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
fn draw_girl_face() {
println!(" _____ ");
println!(" __/ \\__ ");
println!(" _/(o) (o)\\_ ");
println!(" / < \\ ");
println!("| \\___/ |");
println!("| _________ |");
println!(" \\_/ \\__/ ");
println!(" |/\\ /\\ | ");
println!(" | ||| | ");
println!(" | ||| | ");
println!(" |\\/ \\/ | ");
println!(" \\ _/ ");
println!(" \\______/ ");
println!(" || ");
println!(" || ");
println!(" |__| ");
}
fn main() {
println!("\n火焰出现了:");
draw_fire();
println!("\n沐拉出现了:");
draw_girl_face();
let lu = Person { name: "沐拉".to_string(), age: 13, sex: "女".to_string() };
let dog = Animal { name: "小狗".to_string(), age: 3 };
let fire = Fire{address: "森林".to_string()};
println!("{:?}", lu);
println!("{:?}", dog);
lu.attack(&dog);
dog.attack(&lu);
lu.defend(&fire);
dog.defend(&fire);
}测试输出如下:
在这个例子中,特质Fight无需在方法attack中使用where语句罗列可能的类型,而是在Person,Animal中具体说明,这样避免了attack方法看起来可笑。
通过这种方式,rust允许一个特质的方法可以和不同的类型关联起来,而又不会让特质看起来丑陋怪异,难于维护(以后要增加怎么办?)
四、小结
利用关联类型,特质可以更加方便地定义和管理类型,同时也达成了通用的目的。
总体而言,这是一个不错的特性。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]