The goal would be to have something similar to (playground):
trait T {
fn get_mutable_attribute(&mut self) -> &mut String;
fn forwardable_append_attribute(mut self, new_value: &str) -> Self {
let attribute = self.get_mutable_attribute();
attribute.push_str(new_value);
println!("{}", attribute);
self
}
}
struct S {
attribute: String,
}
impl T for S {
fn get_mutable_attribute(&mut self) -> &mut String {
&mut self.attribute
}
}
fn main() {
let s = S {
attribute: "init".to_string(),
}
.forwardable_append_attribute("new_1")
.forwardable_append_attribute("new_2")
.forwardable_append_attribute("new_3");
}
This gives the error:
error[E0277]: the size for values of type `Self` cannot be known at compilation time
--> src/main.rs:3:37
|
3 | fn forwardable_append_attribute(mut self, new_value: &str) -> Self {
| ^^^^^^^^ doesn't have a size known at compile-time
|
= help: unsized locals are gated as an unstable feature
help: consider further restricting `Self`
|
3 | fn forwardable_append_attribute(mut self, new_value: &str) -> Self where Self: std::marker::Sized {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: function arguments must have a statically known size, borrowed types always have a known size
|
3 | fn forwardable_append_attribute(&mut self, new_value: &str) -> Self {
| ^
An alternative would be to define the trait method for each object implementing the trait but that introduces duplication between all sub objects (playground):
trait T {
fn get_mutable_attribute(&mut self) -> &mut String;
fn forwardable_append_attribute(self, new_value: &str) -> Self;
}
struct S {
attribute: String,
}
impl T for S {
fn get_mutable_attribute(&mut self) -> &mut String {
&mut self.attribute
}
fn forwardable_append_attribute(mut self, new_value: &str) -> Self {
let attribute = self.get_mutable_attribute();
attribute.push_str(new_value);
println!("{}", attribute);
self
}
}
fn main() {
let s = S {
attribute: "init".to_string(),
}
.forwardable_append_attribute("new_1")
.forwardable_append_attribute("new_2")
.forwardable_append_attribute("new_3");
}
It's complaining that you can't return self
, since self
might not be Sized
. So, you can just add a bound that Self
must be Sized
on the method:
trait T {
fn get_mutable_attribute(&mut self) -> &mut String;
fn forwardable_append_attribute(mut self, new_value: &str) -> Self
// note this trait bound
where
Self: Sized,
{
let attribute = self.get_mutable_attribute();
attribute.push_str(new_value);
println!("{}", attribute);
self
}
}
struct S {
attribute: String,
}
impl T for S {
fn get_mutable_attribute(&mut self) -> &mut String {
&mut self.attribute
}
}
fn main() {
let s = S {
attribute: "init".to_string(),
}
.forwardable_append_attribute("new_1")
.forwardable_append_attribute("new_2")
.forwardable_append_attribute("new_3");
}
Note that you could also put the Sized
bound on the trait itself instead of the method, which would look like trait T: Sized {...}
. You should do this if the trait only provides the chainable method, so there's no point in implementing the trait for a type that can't have the chainable method. Alternatively, instead of moving self
into the function, you can take a mutable reference and return the mutable reference, removing the need for Sized
:
trait T {
fn get_mutable_attribute(&mut self) -> &mut String;
// note the &mut self and &mut Self
fn forwardable_append_attribute(&mut self, new_value: &str) -> &mut Self {
let attribute = self.get_mutable_attribute();
attribute.push_str(new_value);
println!("{}", attribute);
self
}
}
struct S {
attribute: String,
}
impl T for S {
fn get_mutable_attribute(&mut self) -> &mut String {
&mut self.attribute
}
}
fn main() {
let s = S {
attribute: "init".to_string(),
}
.forwardable_append_attribute("new_1")
.forwardable_append_attribute("new_2")
.forwardable_append_attribute("new_3");
}