Consider I have an external struct Connection
that implements a lot of methods and traits.
In my crate I want to make a special variant of connection SpecialConnection
that adds some minor functionality. (Say 2 extra methods send_special_request
, recv_special_reply
) but for all other intents and purposes SpecialConnection
is just a simple Connection
What is the ideal way to implement this pattern?
If SpecialConnection
has no additional state then there is no need to create a new struct. Just implement a trait called Special
and add the extension methods to it.
pub trait Special {
fn send_special_request(&self);
fn recv_special_reply(&self);
}
impl Special for Connection {
fn send_special_request(&self) {}
fn recv_special_reply(&self) {}
}
But this is no longer sufficient if we need additional state tracked for this two methods to work
pub struct SpecialConnection {
pub conn: Connection,
pub special_props: Vec<String>,
}
impl Special for SpecialConnection {
fn send_special_request(&self) {}
fn recv_special_reply(&self) {}
}
But the problem is apart from these two methods and an additional state SpecialConnection
is exactly the same as Connection
and thus needs to implement all the methods and traits that Connection
does.
Obviously one approach is to expose the internal connection and let the user access it if they need the base functionaility.
let spconn = SpecialConnection::new();
spconn.conn.send_request();
This is what I am doing now. But this is not ideal. I would like a proper implementation of an is-a
relationship. I don't even need selective delegation, I just want to delegate everything to Connection
.
Implementing as_ref
to allow &SpecialConnection
to be coerced into a &Connection
is a possible solution. But I have heard that it's not advisable. Besides, the original Connection
does not implement as_ref
so this is not even possible unless I convince the original crate author to implement it.
There is another option. I can move special_props
as a global lazy static variable using lazy_static
or once_cell
since it only has to be populated once for the lifetime of an application. If I do that I don't need extra state for SpecialConnection
and go back to the Connection
with an extension trait approach. But I am not sure if creating a global variable is a good idea as a library author.
What's the ideal way to do implement this pattern? How do you extend external structs that have lots of methods and traits?