Extract Method Refactoring in Rust

I’m writing a simple utility for manage the /etc/hosts file. I want it in a native language so I can make it SUID, or even better, to lock it down via capabilities. I want to remember how to code in rust. Once I get a simple bit working, I want to refactor. Here’s what I did.

Here is the functioning code:

use std::env;
use std::fs;
 
fn words_by_line<'a>(s: &'a str) -> Vec<Vec<&'a str>> {
    s.lines().map(|line| {
        line.split_whitespace().collect()
    }).collect()
}
 
fn main() {
    let args: Vec<String> = env::args().collect();
    let filename = &args[1];
    let _operation = &args[2];
    let contents = fs::read_to_string(filename)
        .expect("Something went wrong reading the file");
 
    let wbyl = words_by_line(&contents);
    for i in &wbyl {
        for j in i{
            print!("{} ", j);
        }
        println!("");
    }
}
<!-- /wp:html -->
 
<!-- wp:paragraph -->
<p>Build and run with </p>
<!-- /wp:paragraph -->
 
<!-- wp:html -->
<pre>cargo build
./target/debug/hostsman ./hosts list

And it spits out the contents of the local copy of /etc/hosts. We’ll treat this as the unit test for now.

The next step is to start working towards a switch based on the _operation variable. To do this, I want to pull the loop that dumps the file out into its own function. And to do that, I need to figure out the type of the Vector. I use the hack of introducing an error to get the compiler to tell me the type. I change the assignment line to get:

let wbyl: u8 = words_by_line(&amp;contents);

And that tells me:

error[E0308]: mismatched types
  --> src/main.rs:18:20
   |
18 |     let wbyl: u8 = words_by_line(&contents);
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^ expected u8, found struct `std::vec::Vec`
   |
   = note: expected type `u8`
              found type `std::vec::Vec<std::vec::Vec<&str>>`

So I convert the code to use that, build and run. Code now looks like this:

let wbyl: std::vec::Vec&gt; = words_by_line(&amp;
contents)

Now now create a function by copying the existing code block and using the variable type in the parameter list. It looks like this:

use std::env;
use std::fs;
 
fn words_by_line<'a>(s: &'a str) -> Vec<Vec<&'a str>> {
    s.lines().map(|line| {
        line.split_whitespace().collect()
    }).collect()
}
 
fn list(wbyl: std::vec::Vec<std::vec::Vec<&str>>){    
    for i in &wbyl {
        for j in i{
            print!("{} ", j);
        }
        println!("");
    }
}
 
fn main() {
    let args: Vec<String> = env::args().collect();
    let filename = &args[1];
    let _operation = &args[2];
    let contents = fs::read_to_string(filename)
        .expect("Something went wrong reading the file");
 
    let wbyl: std::vec::Vec<std::vec::Vec<&str>> = words_by_line(&contents);
 
    list(wbyl);
}

Now we are prepped to continue development. Next up is to parse the command and execute a different function based on it.

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.