Types 如何在Rust中重用类似但不同类型的代码?
我有一个带有一些功能的基本类型,包括trait实现:Types 如何在Rust中重用类似但不同类型的代码?,types,rust,code-reuse,Types,Rust,Code Reuse,我有一个带有一些功能的基本类型,包括trait实现: use std::fmt; use std::str::FromStr; pub struct MyIdentifier { value: String, } impl fmt::Display for MyIdentifier { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.v
use std::fmt;
use std::str::FromStr;
pub struct MyIdentifier {
value: String,
}
impl fmt::Display for MyIdentifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl FromStr for MyIdentifier {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(MyIdentifier {
value: s.to_string(),
})
}
}
使用std::fmt;
使用std::str::FromStr;
发布结构MyIdentifier{
值:字符串,
}
impl fmt::显示MyIdentifier{
fn fmt(&self,f:&mut fmt::Formatter)->fmt::Result{
写!(f,“{}”,self.value)
}
}
MyIdentifier的impl FromStr{
类型Err=();
fn来自_str(s:&str)->结果{
Ok(我的标识符){
值:s.to_string(),
})
}
}
这是一个简化的例子,实际代码将更加复杂
我想介绍两种类型,它们的字段和行为与我描述的基本类型相同,例如MyUserIdentifier
和MyGroupIdentifier
。为了避免在使用它们时出错,编译器应该将它们视为不同的类型
我不想复制我刚写的全部代码,我想重新使用它。对于面向对象的语言,我将使用继承。如何处理生锈问题?有几种方法可以解决此类问题。下面的解决方案是使用所谓的newtype模式,即newtype包含的对象的统一特征和newtype的特征实现 (解释将是内联的,但如果您希望看到整个代码并同时测试它,请转到。) 首先,我们创建一个描述我们希望从标识符中看到的最小行为的特征。在Rust中,你没有遗传,你有组合,也就是说,一个对象可以实现任何数量的描述其行为的特征。如果你想在你所有的对象中都有一些共同的东西——你可以通过继承来实现——那么你必须为它们实现同样的特性
使用std::fmt;
特征标识符{
fn值(&self)->&str;
}
然后我们创建一个新类型,其中包含一个值,该值是一个泛型类型,它被约束以实现我们的标识符特征。这种模式的好处在于,编译器最终会对其进行优化
struct-Id(T);
现在我们有了一个具体的类型,我们为它实现了Display
trait。由于Id
的内部对象是一个标识符
,因此我们可以在其上调用值
方法,因此我们只需实现此特性一次
impl<T> fmt::Display for Id<T>
where
T: Identifier,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0.value())
}
}
最后但并非最不重要的一点是,您将如何使用它们:
fn main(){
让mid=Id(MyIdentifier(“Hello.to_string());
让uid=Id(MyUserIdentifier{
值:“世界”。到_字符串(),
用户:“Cybran.”to_string(),
});
println!(“{}”,mid);
println!(“{}”,uid);
}
显示很容易,但是我不认为您可以将与str
统一起来,正如我上面的示例所示,很可能不同的标识符有不同的字段,而不仅仅是值
(公平地说,有些甚至没有值
,毕竟标识符
特性只需要对象实现一个名为值
的方法)。从语义上讲,FromStr
应该从字符串构造一个新实例。因此,我将分别为所有类型实现FromStr
。使用a将类型参数添加到您的标识符
。这允许您“标记”给定标识符:
use std::{fmt, marker::PhantomData, str::FromStr};
pub struct Identifier<K> {
value: String,
_kind: PhantomData<K>,
}
impl<K> fmt::Display for Identifier<K> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl<K> FromStr for Identifier<K> {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Identifier {
value: s.to_string(),
_kind: PhantomData,
})
}
}
struct User;
struct Group;
fn main() {
let u_id: Identifier<User> = "howdy".parse().unwrap();
let g_id: Identifier<Group> = "howdy".parse().unwrap();
// do_group_thing(&u_id); // Fails
do_group_thing(&g_id);
}
fn do_group_thing(id: &Identifier<Group>) {}
使用std:{fmt,marker::PhantomData,str::FromStr};
发布结构标识符{
值:字符串,
_种类:幻影数据,
}
impl fmt::标识符的显示{
fn fmt(&self,f:&mut fmt::Formatter)->fmt::Result{
写!(f,“{}”,self.value)
}
}
标识符的impl-FromStr{
类型Err=();
fn来自_str(s:&str)->结果{
Ok(标识符{
值:s.to_string(),
_种类:幻影数据,
})
}
}
结构用户;
结构组;
fn main(){
让u_id:Identifier=“howdy”.parse().unwrap();
让g_id:Identifier=“howdy”.parse().unwrap();
//do_group_thing(&u_id);//失败
执行组任务(g\U id);
}
fn do_group_thing(id:&标识符){}
错误[E0308]:类型不匹配
-->src/main.rs:32:20
|
32 |做集体的事(&u_id);
|^^^应为结构“组”,找到结构“用户”`
|
=注意:应为类型`&标识符`
找到类型`&标识符`
不过,以上这些并不是我自己真正要做的
我想介绍两种具有相同字段和行为的类型
两种类型不应该有相同的行为-它们应该是相同的类型
我不想复制我刚写的全部代码,我想重新使用它
然后就重用它。我们通过将它们组合为更大类型的一部分,一直重用String
和Vec
等类型。这些类型的行为不像String
s或Vec
s,它们只是使用它们
可能标识符是域中的一种基本类型,它应该存在。创建诸如User
或Group
之类的类型,并传递(引用)用户或组。您当然可以添加类型安全性,但这需要程序员承担一定的费用