Creating methods that extend ontop of pre-existing Modules


#1

So bit of a shot in the dark here, Im playing with TcpListener for this example but it could really apply to many others. Say for example i have a listener which im looking to monitor via one line. An example of this is using a method which follows the “unwrap()” or “expect()”.

Heres a sample(obviously doesnt work):
let listener = TcpListener::bind("127.0.0.1:80").unwrap().log("Listener created.");

I’ve done some reading and come across “impl” but i do not know if i can extend ontop of “TcpListener” to allow the above code to work. Is what im looking at even possible or am i just way off?

Any help?


#2

Yeah, you can define a custom trait and implement it for TcpListener. For example:

trait TcpListenerExt {
    fn log<M: AsRef<str>>(self, msg: M) -> Self where Self: Sized {
        println!("{}", msg.as_ref());
        self
    }
}
impl TcpListenerExt for TcpListener {}

fn main() {
    let listener = TcpListener::bind("127.0.0.1:80").unwrap().log("Listener created");
    ...
}

#3

Thats great thanks, while im on the topic is their anyway to extend that to other modules so i can use the same piece of code for multiple modules rather than having to write the trait that extands…

e.g.
TcpListener, TcpStream


#4

Yeah - you can create a blanket impl. Eg:

use std::net::TcpListener;

trait Loggable {
    fn log<M: AsRef<str>>(self, msg: M) -> Self
    where
        Self: Sized,
    {
        println!("{}", msg.as_ref());
        self
    }
}
impl<T> Loggable for T {}

fn main() {
    let listener = TcpListener::bind("127.0.0.1:80")
        .unwrap()
        .log("Listener created");
    let x = "hello".to_string().log("world");
}

Without specialization, however, you won’t be able to customize the log function for some types if you provide the blanket impl. May not matter but keep that in mind.

You can also do a targeted blanket impl, where only types tagged as some trait are eligible. For example:

use std::net::{TcpListener, TcpStream};

trait LogExt {
    fn log<M: AsRef<str>>(self, msg: M) -> Self
    where
        Self: Sized,
    {
        println!("{}", msg.as_ref());
        self
    }
}
trait Loggable {}
impl Loggable for TcpListener {}
impl Loggable for TcpStream {}

impl<T: Loggable> LogExt for T {}

In this case, you have a blanket impl but you can handpick which types should be eligible via the Loggable marker trait. The specialization issue mentioned above still applies.