Extract Function Refactoring using inline functions.

The Extract Function refactoring is the starting point for much of my code clean up. Once a “Main” function gets sufficiently complicated, I pull pieces of it out into their own functions, often with an eye to making them methods of the involved classes.

While working with some rust code, I encountered an opportunity to execute this refactoring on some logging code. Here’s how I executed it.

Here is what the code looks like at the start:

fn main() -> std::io::Result<()> {
    {
        let local_ip4 = IpAddr::from_str("0.0.0.0").unwrap();
        let listen4_port: u16  = 67;
        let socket = UdpSocket::bind(&SocketAddr::new(local_ip4, listen4_port))?;
        socket.set_broadcast(true).expect("set_broadcast call failed");
        // Receives a single datagram message on the socket. If `buf` is too small to hold
        // the message, it will be cut off.
        let mut buf = [0; 300];
        let (_amt, _src) = socket.recv_from(&mut buf)?;
        let boot_packet = build_boot_packet(&buf);
 
        println!("packet received");
        println!("opcode      = {0}", boot_packet.opcode);
        println!("hwtype      = {0}", boot_packet.hwtype);
        println!("hw addr len = {0}", boot_packet.hw_addr_len);
        println!("hop count   = {0}", boot_packet.hop_count);            
        println!("txn_id      = {:x}", boot_packet.txn_id);            
        println!("num_secs    = {:}", boot_packet.num_secs);
        println!("ips {0} {1} {2} {3}",
                 boot_packet.client_ip, boot_packet.your_ip,
                 boot_packet.server_ip,  boot_packet.gateway_ip);
        println!("Mac Addr:   = {:}", boot_packet.client_mac);            
   Ok(())
}

My next task is to modify the data in the packet in order to send it back to the client. I know that I am going to want to log the data prior to writing it to the socket. That means I am going to end up needing those same log lines, possibly with additional entries.

I also know that I am going to want to see the difference when logging the packets. So…I decide first to implement the copy trait on my structure, and make a duplicate of the packet so I can see the before and after.

        let boot_request = build_boot_packet(&amp;buf);
        let mut boot_packet = boot_request;

As always: run the code after making this change. I don’t have automated unit tests for this yet, as all I am doing is readying data off the wire…just saying that points at the direction for where my test should go, and I will do that shortly.

For now, I spin up a virtual machine that makes a DHCP request, and I make sure that I can still see the log data.

Now, to extract the logging code, I first wrap that section in a local function. Make sure to call the function after it is defined:

        fn log_packet(boot_packet: &amp;BootPacket){
        println!("packet received");
        println!("opcode      = {0}", boot_packet.opcode);
        println!("hwtype      = {0}", boot_packet.hwtype);
        println!("hw addr len = {0}", boot_packet.hw_addr_len);
        println!("hop count   = {0}", boot_packet.hop_count);
        println!("txn_id      = {:x}", boot_packet.txn_id);
        println!("num_secs    = {:}", boot_packet.num_secs);
        println!("ips {0} {1} {2} {3}",
                 boot_packet.client_ip, boot_packet.your_ip,
                 boot_packet.server_ip,  boot_packet.gateway_ip);
        println!("Mac Addr:   = {:}", boot_packet.client_mac);
        }
        log_packet(&amp;boot_packet);

Again, run the test. If it runs successfully, continue on to moving the log_packet function up to the file level namespace. Run the tests.

The above steps are the main technique, and can be used to extract a function. I am going to take it one step further and convert it to a method implemented on struct BootPacket. The final code looks like this:

impl BootPacket {
    fn log_packet(&amp;self){
        println!("packet received");
        println!("opcode      = {0}", self.opcode);
        println!("hwtype      = {0}", self.hwtype);
        println!("hw addr len = {0}", self.hw_addr_len);
        println!("hop count   = {0}", self.hop_count);
        println!("txn_id      = {:x}", self.txn_id);
        println!("num_secs    = {:}", self.num_secs);
        println!("ips {0} {1} {2} {3}",
                 self.client_ip, self.your_ip,
                 self.server_ip,  self.gateway_ip);
        println!("Mac Addr:   = {:}", self.client_mac);
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.