Traits can't provide or require struct fields. Though there is an RFC (#1546) about allowing fields in traits. However, there isn't any unstable features allowing this (yet?).
You can still simplify what you're trying to do though. I've taken the liberty to rename and change your trait, to be able to provide more thorough examples.
Let's consider that we have a Jobs trait. Which defines various methods, that all requires the jobs: Vec<String> field.
trait Jobs {
fn add_job(&mut self, job: String);
fn clear_jobs(&mut self);
fn count_jobs(&self) -> usize;
}
Using a macro
One solution could be to use a macro, which implements all those methods.
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()
}
}
)+)
}
Then you can easily reuse the code, by using the macro.
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);
Using a second HasJobs trait
Another solution could be to introduce a second HasJobs trait. Then you can use a blanket implementation for Jobs if a type implements HasJobs.
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()
}
}
Now HasJobs still needs to be implemented for all your types. But if Jobs has a significant amount of methods. Then implementing HasJobs is a lot easier to deal with. Which we can also do using a macro:
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
}
}
)+)
}
Then once again, you just do:
struct Person {
jobs: Vec<String>,
}
struct Customer {
jobs: Vec<String>,
}
impl_has_jobs!(Person);
impl_has_jobs!(Customer);
// or
impl_has_jobs!(Person, Customer);
Using Deref and DerefMut
Lastly, if Customer is always a Person, then you could change Person into a struct and use composition, i.e. add person: Person to Customer (and other types).
So first, 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()
}
}
Then now you can use Deref and DerefMut to dereference Customer to Person. Thus all Person's methods are available directly through Customer.
However, this solution only works if you only have one "trait", i.e. you can only Deref Customer to Person. You wouldn't be able to also Deref Customer to 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
}
}