Traceroute implementation in Rust I made this simple traceroute implementation using the libpnet library, which sends ICMP echo probes with an increasing time-to-live, and then outputs the IPs of the hosts replying.

I'm new to Rust, so any style/performance tips would be appreciated!

extern crate pnet;

use std::env;
use std::error;
use std::net;
use std::str::FromStr;

use pnet::packet::icmp::echo_request::MutableEchoRequestPacket;
use pnet::packet::icmp::IcmpTypes;
use pnet::packet::ip::IpNextHeaderProtocols;
use pnet::packet::ipv4::MutableIpv4Packet;
use pnet::packet::MutablePacket;
use pnet::transport::{icmp_packet_iter, transport_channel, TransportChannelType::Layer3};
use pnet::util;

type Result&lt;T&gt; = std::result::Result&lt;T, Box&lt;error::Error&gt;&gt;;

static IPV4_HEADER_LEN: usize = 21;
static ICMP_HEADER_LEN: usize = 8;
static ICMP_PAYLOAD_LEN: usize = 32;

fn main() {
    std::process::exit(match run_app() {
        Ok(_) =&gt; 0,
        Err(error) =&gt; {
            eprintln!("Error: {}", error);
            1
        }
    });
}

fn run_app() -&gt; Result&lt;()&gt; {
    let args: Vec&lt;String&gt; = env::args().collect();
    match args.len() {
        2 =&gt; {
            let protocol = Layer3(IpNextHeaderProtocols::Icmp);
            let (mut tx, mut rx) = transport_channel(1024, protocol)
                .map_err(|err| format!("Error opening the channel: {}", err))?;

            let ip_addr = net::Ipv4Addr::from_str(&amp;args[1]).map_err(|_| "Invalid address")?;
            let mut rx = icmp_packet_iter(&amp;mut rx);
            let mut ttl = 4;
            let mut prev_addr = None;
            loop {
                let mut buffer_ip = [0u8; 40];
                let mut buffer_icmp = [0u8; 40];
                let mut icmp_packet =
                    create_icmp_packet(&amp;mut buffer_ip, &amp;mut buffer_icmp, ip_addr, ttl)?;
                tx.send_to(icmp_packet, net::IpAddr::V4(ip_addr))?;
                if let Ok((_, addr)) = rx.next() {
                    if Some(addr) == prev_addr {
                        return Ok(());
                    }
                    prev_addr = Some(addr);
                    // This is not quite ideal as replies may arrive in different order 
                    // than they were sent in, but using the identification field
                    // would make this simple example more complex
                    println!("TTL: {} - {:?}", ttl, addr.to_string());
                }
                ttl += 1;
            }
        }
        _ =&gt; Err((format!("Usage: {} ip", args[0])).into()),
    }
}

fn create_icmp_packet&lt;'a&gt;(
    buffer_ip: &amp;'a mut [u8],
    buffer_icmp: &amp;'a mut [u8],
    dest: net::Ipv4Addr,
    ttl: u8,
) -&gt; Result&lt;MutableIpv4Packet&lt;'a&gt;&gt; {
    let mut ipv4_packet = MutableIpv4Packet::new(buffer_ip).unwrap();
    ipv4_packet.set_version(4);
    ipv4_packet.set_header_length(IPV4_HEADER_LEN as u8);
    ipv4_packet.set_total_length((IPV4_HEADER_LEN + ICMP_HEADER_LEN + ICMP_PAYLOAD_LEN) as u16);
    ipv4_packet.set_ttl(ttl);
    ipv4_packet.set_next_level_protocol(IpNextHeaderProtocols::Icmp);
    ipv4_packet.set_destination(dest);

    let mut icmp_packet = MutableEchoRequestPacket::new(buffer_icmp).unwrap();
    icmp_packet.set_icmp_type(IcmpTypes::EchoRequest);
    let checksum = util::checksum(&amp;icmp_packet.packet_mut(), 2);
    icmp_packet.set_checksum(checksum);
    ipv4_packet.set_payload(icmp_packet.packet_mut());
    Ok(ipv4_packet)
}
I love your program’s entry point. Nearly all of my F# CLIs have an entrypoint that look nearly identical. However, Rust will automatically convert a Result into an exit code for you.

You can get rid of your run_app and wrapper by inclining it and modifying main’s signature. 

fn main() -&gt; Result&lt;(), Box&lt;dyn Error&gt;&gt; {
   // contents of `run_app` method
}


I believe this will even automatically print out the error.