Getting "`()` is not an iterator" error while traversing on a vector

I'm completely new to Rust and have been learning it for the past two weeks from the book "The Rust Programming Language".
In chapter 12 "An I/O Project: Building a command-line program" the author writes a certain search function that is supposed to return a vector of &str's, and the contents of this vector will then be iterated over and printed in the calling function.

Unfortunately I'm encountering this error while running cargo run to sample.txt

   Compiling rust_grep v0.1.0 (/home/atulu/Documents/rust_grep)
error[E0277]: `()` is not an iterator
  --> src/lib.rs:46:17
   |
46 |     for line in results {
   |                 ^^^^^^^ `()` is not an iterator
   |
   = help: the trait `std::iter::Iterator` is not implemented for `()`
   = note: required by `std::iter::IntoIterator::into_iter`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
error: could not compile `rust_grep`.

To learn more, run the command again with --verbose.

Here is the code in lib.rs:

use std::fs;
use std::env;
use std::error::Error;

pub struct Config {                     
    pub query: String,          //note here, the String is not of reference
    pub filename: String,       //so the String value passed here should be owned
    pub case_sensitive: bool,   //owned by Config struct instance.
}                         


impl Config {
    pub fn new(args: &[String]) -> Result<Config, &'static str> {
        if args.len() < 3 {
            return Err("Not enough arguments entered, Aborting!");
        }

        let query = args[1].clone();
        let filename = args[2].clone();
        let case_sensitive = env::var("CASE_INSENSITIVE").is_err();

        Ok(Config { query, filename, case_sensitive })
    }
}


pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
    let contents = fs::read_to_string(config.filename)?;

    let results  = if config.case_sensitive {
        search(&config.query, &contents);
    } else {
        search_case_insensitive(&config.query, &contents);
    };

    for line in results {
        println!("{}", line);
    }

    Ok(())
}


pub fn search<'a> (query: &str, contents: &'a str) -> Vec<&'a str> {
    let mut results = Vec::new();

    for line in contents.lines() {
        if line.contains(query) {
            results.push(line);
        }
    }

    results
}


pub fn search_case_insensitive<'a> (query: &str, contents: &'a str) -> Vec<&'a str> {
    let query = query.to_lowercase(); //to_lowercase creates new String here
    let mut results = Vec::new();

    for line in contents.lines() {
        if line.to_lowercase().contains(&query) {
            results.push(line);
        }
    }

    results
}

I don't understand why the results variable in the run() function is getting the () type there as I'm trying to return a vector from the search functions.

I have a feeling that I'm making a newbie mistake here but I'm not able to figure it out.

Thanks in advance!

The results variable is assigned an if expression of which both blocks end with a semicolon statement. Semicolons cause statements to evaluate and then throw away their expression, and yield () (the unit type, with no information, that other languages erroneously call void).

Remove the semicolons, and your code will compile:

let results  = if config.case_sensitive {
    search(&config.query, &contents)
} else {
    search_case_insensitive(&config.query, &contents)
};
1 Like

Oh boy, that indeed was a silly mistake. Using semi-colons have been engraved into my muscle-memory. :sweat_smile:

Thank you for pointing it out!

1 Like