A simple implementation of the Unix find command written in Rust I'm a C++ developer trying to learn Rust for fun and XP.
I decided to implement some of the Unix tools for practice.

Below is a simplified implementation of the find command.  The program takes 0 to 2 arguments: The first being the file to find, the second being the directory to start searching (I'm aware that the args aren't very robust).

I get the feeling there is a more succinct way to do what I'm trying to accomplish.

use std::env;                                                                                                                                                                                  
use std::io;                                                                                                                                                                                   
use std::collections::VecDeque;                                                                                                                                                                
use std::ffi::OsString;                                                                                                                                                                        
use std::path::PathBuf;                                                                                                                                                                        
use std::process::exit;                                                                                                                                                                        

fn find(query: &amp;String, start: &amp;OsString) -&gt; io::Result&lt;Vec&lt;PathBuf&gt;&gt; {                                                                                                                        
    let start = PathBuf::from(start);                                                                                                                                                          
    let mut dirs = VecDeque::from(vec![start]);                                                                                                                                                
    let mut result = Vec::new();                                                                                                                                                               

    while let Some(dir) = dirs.pop_front() {
        for entry in dir.read_dir()? {
            let path = entry?.path();
            if path.is_dir() {
                dirs.push_back(path.clone());
            }
            if let Some(name) = path.file_name() {
                if query.is_empty() || query.as_str() == name {
                    result.push(path.clone());
                }
            }
        }
    }
    Ok(result)
}
fn main() {
    let query = match env::args().nth(1) {
        Some(query) =&gt; query,
        None =&gt; String::new(),
    };
    let start = match env::args().nth(2) {
        Some(start) =&gt; OsString::from(start),
        None =&gt; OsString::from("."),
    };

    match find(&amp;query, &amp;start) {
        Ok(paths) =&gt; {
            for path in paths {
                if let Some(p) = path.to_str() {
                    println!("{}", p);
                }
            }
        },
        Err(err) =&gt; {
            eprintln!("Error: {}", err);
            exit(1);
        },
    };
}
Your code can be simplified a little bit:

use std::collections::VecDeque;
use std::ffi::{OsStr, OsString};
use std::io;
use std::path::PathBuf;

fn find(query: &amp;str, start: &amp;OsStr) -&gt; io::Result&lt;Vec&lt;PathBuf&gt;&gt; {
    let start = PathBuf::from(start);
    let mut dirs = VecDeque::from(vec![start]);
    let mut result = Vec::new();

    while let Some(dir) = dirs.pop_front() {
        for entry in dir.read_dir()? {
            let path = entry?.path();
            if path.is_dir() {
                dirs.push_back(path.clone());
            }
            if let Some(name) = path.file_name() {
                if query.is_empty() || query == name {
                    result.push(path.clone());
                }
            }
        }
    }
    Ok(result)
}

fn main() -&gt; io::Result&lt;()&gt; {
    let mut args = std::env::args().skip(1);
    let query = args.next().unwrap_or(String::new());
    let start = args.next().map(OsString::from).unwrap_or(OsString::from("."));

    for path in find(&amp;query, &amp;start)? {
        if let Some(p) = path.to_str() {
            println!("{}", p);
        }
    }
    Ok(())
}


In your function find, you should use the borrowed version of String and OsString as parameters; or you can just move the value if you think that query or start will not be needed anymore in the main.

In you main, you can take advantage of the Iterator's and Option's methods to make a more concise and cleaner code:


You create an iterator that will iterate over all args but the first using skip;
The query is the next item: if there is no item, you give a default value with unwrap_or;
For the next item, first you apply a function to the inner with map, and then you provide a default value;
Your main can return a Result, so you do not need to make the error handling by hand.


Just a little thing: the idiomatic way to do such an algorithm in Rust is to implement an Iterator. In you code you do an unnecessary allocation for all your found files.