具有默认实现和必需结构成员的特征
Trait with default implementation and required struct member
我有一个 rust trait,它应该为 vector 添加一个值。为了使 add_job
函数正常工作,必须确保在为具体结构实现特征时向量存在。
下面的代码当然会失败,因为作业从未实现过。只是为了证明我的意图:
trait Person {
// default implementation of add job
fn add_job(&self, job: String) {
self.jobs.push(job)
}
}
struct Customer {
// add_job is used as default implementation
// provided by trait
}
impl Person for Customer {
// some stuff
}
fn main() {
let mut george = Customer {};
george.add_job("programmer".to_string());
}
有没有办法让一个特征同时提供结构成员?
可能不是,但是解决上述问题的“生锈”方法是什么?
特征不能提供或要求结构字段。虽然有一个 RFC (#1546) 关于允许特征中的字段。但是,没有任何不稳定的功能允许这样做(还没有?)。
尽管如此,您仍然可以简化您正在尝试做的事情。我冒昧地重命名和更改了您的特征,以便能够提供更详尽的示例。
假设我们有一个 Jobs
特征。其中定义了各种方法,所有方法都需要 jobs: Vec<String>
字段。
trait Jobs {
fn add_job(&mut self, job: String);
fn clear_jobs(&mut self);
fn count_jobs(&self) -> usize;
}
使用宏
一个解决方案可能是使用 macro
,它实现了所有这些方法。
macro_rules! impl_jobs_with_field {
($($t:ty),+ $(,)?) => ($(
impl Jobs for $t {
fn add_job(&mut self, job: String) {
self.jobs.push(job);
}
fn clear_jobs(&mut self) {
self.jobs.clear();
}
fn count_jobs(&self) -> usize {
self.jobs.len()
}
}
)+)
}
然后您可以使用宏轻松地重用代码。
struct Person {
jobs: Vec<String>,
}
struct Customer {
jobs: Vec<String>,
}
impl_jobs_with_field!(Person);
impl_jobs_with_field!(Customer);
// or
impl_jobs_with_field!(Person, Customer);
使用第二个 HasJobs
特征
另一种解决方案是引入第二个 HasJobs
特征。如果一个类型实现了 HasJobs
.
,那么你可以对 Jobs
使用 blanket implementation
trait HasJobs {
fn jobs(&self) -> &[String];
fn jobs_mut(&mut self) -> &mut Vec<String>;
}
impl<T: HasJobs> Jobs for T {
fn add_job(&mut self, job: String) {
self.jobs_mut().push(job);
}
fn clear_jobs(&mut self) {
self.jobs_mut().clear();
}
fn count_jobs(&self) -> usize {
self.jobs().len()
}
}
现在 HasJobs
仍然需要为您的所有类型实施。但是如果 Jobs
有大量的方法。然后实施 HasJobs
就容易多了。我们也可以使用宏来做到这一点:
macro_rules! impl_has_jobs {
($($t:ty),+ $(,)?) => ($(
impl HasJobs for $t {
fn jobs(&self) -> &[String] {
&self.jobs
}
fn jobs_mut(&mut self) -> &mut Vec<String> {
&mut self.jobs
}
}
)+)
}
然后再一次,你只需做:
struct Person {
jobs: Vec<String>,
}
struct Customer {
jobs: Vec<String>,
}
impl_has_jobs!(Person);
impl_has_jobs!(Customer);
// or
impl_has_jobs!(Person, Customer);
使用 Deref
和 DerefMut
最后,如果 Customer
始终是 Person
,那么您可以将 Person
更改为 struct
并使用组合,即将 person: Person
添加到Customer
(和其他类型)。
那么首先,impl Jobs for Person
:
struct Person {
jobs: Vec<String>,
}
impl Jobs for Person {
fn add_job(&mut self, job: String) {
self.jobs.push(job);
}
fn clear_jobs(&mut self) {
self.jobs.clear();
}
fn count_jobs(&self) -> usize {
self.jobs.len()
}
}
那么现在您可以使用 Deref
and DerefMut
将 Customer
取消引用到 Person
。因此所有 Person
的方法都可以直接通过 Customer
.
获得
但是,如果您只有一个“特征”,即您只能 Deref
Customer
至 Person
,则此解决方案仅 有效 .你也不能 Deref
Customer
到 SomethingElse
.
use std::ops::{Deref, DerefMut};
struct Customer {
person: Person,
}
impl Deref for Customer {
type Target = Person;
fn deref(&self) -> &Self::Target {
&self.person
}
}
impl DerefMut for Customer {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.person
}
}
我有一个 rust trait,它应该为 vector 添加一个值。为了使 add_job
函数正常工作,必须确保在为具体结构实现特征时向量存在。
下面的代码当然会失败,因为作业从未实现过。只是为了证明我的意图:
trait Person {
// default implementation of add job
fn add_job(&self, job: String) {
self.jobs.push(job)
}
}
struct Customer {
// add_job is used as default implementation
// provided by trait
}
impl Person for Customer {
// some stuff
}
fn main() {
let mut george = Customer {};
george.add_job("programmer".to_string());
}
有没有办法让一个特征同时提供结构成员?
可能不是,但是解决上述问题的“生锈”方法是什么?
特征不能提供或要求结构字段。虽然有一个 RFC (#1546) 关于允许特征中的字段。但是,没有任何不稳定的功能允许这样做(还没有?)。
尽管如此,您仍然可以简化您正在尝试做的事情。我冒昧地重命名和更改了您的特征,以便能够提供更详尽的示例。
假设我们有一个 Jobs
特征。其中定义了各种方法,所有方法都需要 jobs: Vec<String>
字段。
trait Jobs {
fn add_job(&mut self, job: String);
fn clear_jobs(&mut self);
fn count_jobs(&self) -> usize;
}
使用宏
一个解决方案可能是使用 macro
,它实现了所有这些方法。
macro_rules! impl_jobs_with_field {
($($t:ty),+ $(,)?) => ($(
impl Jobs for $t {
fn add_job(&mut self, job: String) {
self.jobs.push(job);
}
fn clear_jobs(&mut self) {
self.jobs.clear();
}
fn count_jobs(&self) -> usize {
self.jobs.len()
}
}
)+)
}
然后您可以使用宏轻松地重用代码。
struct Person {
jobs: Vec<String>,
}
struct Customer {
jobs: Vec<String>,
}
impl_jobs_with_field!(Person);
impl_jobs_with_field!(Customer);
// or
impl_jobs_with_field!(Person, Customer);
使用第二个 HasJobs
特征
另一种解决方案是引入第二个 HasJobs
特征。如果一个类型实现了 HasJobs
.
Jobs
使用 blanket implementation
trait HasJobs {
fn jobs(&self) -> &[String];
fn jobs_mut(&mut self) -> &mut Vec<String>;
}
impl<T: HasJobs> Jobs for T {
fn add_job(&mut self, job: String) {
self.jobs_mut().push(job);
}
fn clear_jobs(&mut self) {
self.jobs_mut().clear();
}
fn count_jobs(&self) -> usize {
self.jobs().len()
}
}
现在 HasJobs
仍然需要为您的所有类型实施。但是如果 Jobs
有大量的方法。然后实施 HasJobs
就容易多了。我们也可以使用宏来做到这一点:
macro_rules! impl_has_jobs {
($($t:ty),+ $(,)?) => ($(
impl HasJobs for $t {
fn jobs(&self) -> &[String] {
&self.jobs
}
fn jobs_mut(&mut self) -> &mut Vec<String> {
&mut self.jobs
}
}
)+)
}
然后再一次,你只需做:
struct Person {
jobs: Vec<String>,
}
struct Customer {
jobs: Vec<String>,
}
impl_has_jobs!(Person);
impl_has_jobs!(Customer);
// or
impl_has_jobs!(Person, Customer);
使用 Deref
和 DerefMut
最后,如果 Customer
始终是 Person
,那么您可以将 Person
更改为 struct
并使用组合,即将 person: Person
添加到Customer
(和其他类型)。
那么首先,impl Jobs for Person
:
struct Person {
jobs: Vec<String>,
}
impl Jobs for Person {
fn add_job(&mut self, job: String) {
self.jobs.push(job);
}
fn clear_jobs(&mut self) {
self.jobs.clear();
}
fn count_jobs(&self) -> usize {
self.jobs.len()
}
}
那么现在您可以使用 Deref
and DerefMut
将 Customer
取消引用到 Person
。因此所有 Person
的方法都可以直接通过 Customer
.
但是,如果您只有一个“特征”,即您只能 Deref
Customer
至 Person
,则此解决方案仅 有效 .你也不能 Deref
Customer
到 SomethingElse
.
use std::ops::{Deref, DerefMut};
struct Customer {
person: Person,
}
impl Deref for Customer {
type Target = Person;
fn deref(&self) -> &Self::Target {
&self.person
}
}
impl DerefMut for Customer {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.person
}
}