如何使用通用函数减少重复代码

时间:2019-09-02 08:50:46

标签: rust

我有很多类似下面的代码:

let metadata = Some(meta::ObjectMeta {
    name: Some(apple.name.clone()),
}),
let metadata = Some(meta::ObjectMeta {
    name: Some(orange.name.clone()),
}),

我希望编写一个通用函数,例如:

fn get_meta<T>(args: T) -> Option<meta::ObjectMeta> {
   Some(meta::ObjectMeta {
       name: Some(args.name.clone()),
   })
}
let metadata = get_meta(self);

但是如何确定泛型T具有字段name

2 个答案:

答案 0 :(得分:3)

当像您一样引入通用类型T时,您将无法执行任何操作:您无法调用方法,无法访问字段等。您只是对类型一无所知。将“功能”添加到类型的唯一方法是通过特征范围,例如如果<T: Foo>是特征,则Foo

因此,如果要抽象化行为,则必须编写一个捕获此行为的特征。您遇到的问题是:特征不允许指定字段。换句话说:您不能说“实现此特征的所有类型都必须具有字段name。但是,您当然可以创建 getter方法(在Rust中,通常称为没有get)的字段:

trait HasName {
    fn name(&self) -> &str;
}

fn get_meta<T: HasName>(args: T) -> Option<meta::ObjectMeta> {
   Some(meta::ObjectMeta {
       name: Some(args.name().clone()),
   })
}

使用吸气剂方法代替字段应该在基本上所有情况下都可以使用。


作为旁注:some early considerations关于向特征添加字段。但是,该讨论还处于早期阶段。甚至没有正式的RFC,所以不要指望很快就使用此功能。

答案 1 :(得分:1)

您不能。已经提出了这样的主张-在特征中具有虚拟领域-但目前还没有任何人接受。不过,您可以使用吸气剂,并进行一些多态处理:

struct Apple;

struct Orange;

trait Named {
    fn name(&self) -> &str;
}

impl Named for Apple {
    fn name(&self) -> &str {
        "Golden"
    }
}

impl Named for Orange {
    fn name(&self) -> &str {
        "Orange"
    }
}

fn get_name<T>(named: T) -> String where T: Named {
    named.name().to_owned()
}

fn main() {
    assert_eq!(get_name(Apple), "Golden");
    assert_eq!(get_name(Orange), "Orange");
}