Fairly generic method chaining is possible through BorrowMut, but the construction is not as ergonomic as it could be, because type inference forces us to be extra explicit:
use std::borrow::BorrowMut;
#[derive(Debug)]
struct Foo {
bar: usize
}
impl Foo {
fn new(bar: usize) -> Self {
Self { bar }
}
}
// We can implement relatively nice method chaining through
// implementing functions in a helper trait
trait FooImpl: BorrowMut<Foo> + Sized {
fn set_bar(mut self, bar: usize) -> Self {
self.borrow_mut().bar = bar;
self
}
}
impl<T: BorrowMut<Foo> + Sized> FooImpl for T {}
fn main() {
let mut x = Foo::new(0).set_bar(1);
// Unfortunately, using the helper trait is not quite straightforward:
// x.set_bar(2); // error[E0382]: borrow of moved value: `x`
// I guess the rust compiler infers the type of self to be `Foo` instead of `&mut Foo`,
// so we have to make it explicit
(&mut x).set_bar(2);
// Though even with this alteration, there are still obstackles, e.g. using this directly with Box does not
// work because &mut Box<T> does not implement BorrowMut<T>
let mut y = Box::new(Foo::new(0).set_bar(1));
// y.set_bar(2); // error[E0382]: borrow of moved value: `y`
// (&mut y).set_bar(2); // error[E0507]: cannot move out of a mutable reference
y.as_mut().set_bar(2);
println!("x={x:?} y={y:?}");
}
// If the rust compiler supported BorrowMut as an allowed type for self, we could make the entire
// construction even more ergonomic:
// impl Foo {
// fn set_bar<T: BorrowMut<Self>>(mut self: T, bar: usize) -> T { // error[E0307]: invalid `self` parameter type: `T`
// self.borrow_mut().bar = bar;
// self
// }
// }
You are over-engineering the API. There is value in keeping it simple, and only using generics when required. The following doesn't support chaining, but it is dead-simple, easy to understand, and works with owned and borrowed values (including &mut Box<Foo>) thanks to Deref coercion: