Подготовим пример: есть рыцарь и маг, оба могут держать оружие, но оружие у каждого своё.
// Define a weapon type
structSword{damage: u32,}structWand{magic_power: u32,}// This trait says "I can hold some type of item W"
// The <W> is a placeholder waiting to be filled
traitCanHold<W>{fnhold(&self,item: W);fndescribe_held_item(&self)-> String;}
В данном случае c параметром типаж как пустой стикер или пустой знак отличия, где поле ещё не заполнено конкретикой. Заполним конкретику:
structKnight{name: String,}structWizard{name: String,}// The knight can hold a Sword specifically
implCanHold<Sword>forKnight{fnhold(&self,item: Sword){println!("{} grips the sword firmly! Damage: {}",self.name,item.damage);}fndescribe_held_item(&self)-> String{String::from("A heavy blade")}}// The wizard can hold a Wand specifically
implCanHold<Wand>forWizard{fnhold(&self,item: Wand){println!("{} twirls the wand gracefully! Power: {}",self.name,item.magic_power);}fndescribe_held_item(&self)-> String{String::from("A mystical wand")}}
Теперь добавим, что рыцарь может держать в руках ещё щит - и это тоже попадает в типаж, остаётся лишь +1 параметр добавить:
structShield{defense: u32,}// The knight can ALSO hold a Shield!
implCanHold<Shield>forKnight{fnhold(&self,item: Shield){println!("{} raises the shield! Defense: {}",self.name,item.defense);}fndescribe_held_item(&self)-> String{String::from("A sturdy shield")}}fnmain(){letknight=Knight{name: String::from("Arthur")};// The same knight can hold different items!
knight.hold(Sword{damage: 50});// Uses CanHold<Sword>
knight.hold(Shield{defense: 30});// Uses CanHold<Shield> - same trait, different weapon!
}
Без параметра типаж работает НЕ БУДЕТ: потому что нельзя тот же типаж CanHold несколько раз применять просто так. А с параметром - МОЖНО, главное, чтобы параметр W отличался. Тогда один рыцарь может иметь несколько знаков из одной семьи знаков.
// AsRef is defined like this (simplified):
traitAsRef<T>{fnas_ref(&self)-> &T;// "Turn myself into a reference to T"
}// String implements AsRef<str> - "I can act as a string slice reference"
implAsRef<str>forString{fnas_ref(&self)-> &str{// String can easily give a &str view of its contents
&self[..]}}// &str also implements AsRef<str> - "I'm already a str reference!"
implAsRef<str>for&str{fnas_ref(&self)-> &str{self// Just return myself, I'm already what you want
}}// PathBuf (a file path type) implements AsRef<Path>
implAsRef<Path>forPathBuf{fnas_ref(&self)-> &Path{// Give a Path view of the PathBuf
self.as_path()}}
то это значит “приму любой тип N, если у N есть знак/стикер AsRef”, и это означает, что N может превратиться в &str.
Пример с превращениями
Представим игру, где персонажи могут превращаться в драконов или зайцев:
structPlayer{name: String,}structDragon{fire_power: u32,}structRabbit{speed: u32,}// The polymorph trait: "I can transform into T"
traitPolymorph<T>{fntransform(&self)-> T;}// Player can polymorph into a Dragon
implPolymorph<Dragon>forPlayer{fntransform(&self)-> Dragon{println!("{} grows scales and wings!",self.name);Dragon{fire_power: 100}}}// Player can ALSO polymorph into a Rabbit
implPolymorph<Rabbit>forPlayer{fntransform(&self)-> Rabbit{println!("{} shrinks and grows fluffy ears!",self.name);Rabbit{speed: 200}}}fnmain(){letplayer=Player{name: String::from("Alex")};// Same player, same trait name, completely different transformations!
letdragon_form=player.transform();// Rust infers we want Dragon here
letrabbit_form: Rabbit=player.transform();// Explicitly ask for Rabbit
}