about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorManish Goregaokar <manishsmail@gmail.com>2016-05-09 12:50:21 -0700
committerManish Goregaokar <manishsmail@gmail.com>2016-05-09 14:59:40 -0700
commit35cc6b0c2cd15a3fe0cdbf86caec9fd6bb9bc6d5 (patch)
treec02e0e0e3684c0e1b3b1ef4b0036bd2c576aa313 /src
parentfffaf665f2bdab15c2ea11d9f97df22c401c9f36 (diff)
parentd75c079cdeb6bfd1938ab24d061025c2b54aebab (diff)
downloadrust-35cc6b0c2cd15a3fe0cdbf86caec9fd6bb9bc6d5.tar.gz
rust-35cc6b0c2cd15a3fe0cdbf86caec9fd6bb9bc6d5.zip
Rollup merge of #33480 - birkenfeld:issue-33422, r=steveklabnik
book: fixup code in error handling tutorial

A few oversights happened while porting the example from docopt to getopts.  I retraced all the steps, fixing code and description as necessary.

Fixes: #33422
Diffstat (limited to 'src')
-rw-r--r--src/doc/book/error-handling.md120
1 files changed, 61 insertions, 59 deletions
diff --git a/src/doc/book/error-handling.md b/src/doc/book/error-handling.md
index 12cb71973ab..a10e98fac7a 100644
--- a/src/doc/book/error-handling.md
+++ b/src/doc/book/error-handling.md
@@ -1573,8 +1573,9 @@ detail on Getopts, but there is [some good documentation][15]
 describing it. The short story is that Getopts generates an argument
 parser and a help message from a vector of options (The fact that it
 is a vector is hidden behind a struct and a set of methods). Once the
-parsing is done, we can decode the program arguments into a Rust
-struct. From there, we can get information about the flags, for
+parsing is done, the parser returns a struct that records matches
+for defined options, and remaining "free" arguments.
+From there, we can get information about the flags, for
 instance, whether they were passed in, and what arguments they
 had. Here's our program with the appropriate `extern crate`
 statements, and the basic argument setup for Getopts:
@@ -1605,8 +1606,8 @@ fn main() {
         print_usage(&program, opts);
         return;
     }
-    let data_path = &args[1];
-    let city = &args[2];
+    let data_path = &matches.free[0];
+    let city: &str = &matches.free[1];
 
     // Do stuff with information
 }
@@ -1680,8 +1681,8 @@ fn main() {
         return;
     }
 
-    let data_path = &args[1];
-    let city: &str = &args[2];
+    let data_path = &matches.free[0];
+    let city: &str = &matches.free[1];
 
     let file = File::open(data_path).unwrap();
     let mut rdr = csv::Reader::from_reader(file);
@@ -1792,13 +1793,15 @@ fn main() {
         Ok(m)  => { m }
         Err(e) => { panic!(e.to_string()) }
     };
+
     if matches.opt_present("h") {
         print_usage(&program, opts);
         return;
     }
 
-    let data_path = &args[1];
-    let city = &args[2];
+    let data_path = &matches.free[0];
+    let city: &str = &matches.free[1];
+
     for pop in search(data_path, city) {
         println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
     }
@@ -1876,14 +1879,14 @@ when calling `search`:
 
 ```rust,ignore
 ...
-match search(&data_file, &city) {
-    Ok(pops) => {
-        for pop in pops {
-            println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
+    match search(data_path, city) {
+        Ok(pops) => {
+            for pop in pops {
+                println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
+            }
         }
+        Err(err) => println!("{}", err)
     }
-    Err(err) => println!("{}", err)
-}
 ...
 ```
 
@@ -1914,43 +1917,37 @@ fn print_usage(program: &str, opts: Options) {
     println!("{}", opts.usage(&format!("Usage: {} [options] <city>", program)));
 }
 ```
-The next part is going to be only a little harder:
+Of course we need to adapt the argument handling code:
 
 ```rust,ignore
 ...
-let mut opts = Options::new();
-opts.optopt("f", "file", "Choose an input file, instead of using STDIN.", "NAME");
-opts.optflag("h", "help", "Show this usage message.");
-...
-let file = matches.opt_str("f");
-let data_file = &file.as_ref().map(Path::new);
-
-let city = if !matches.free.is_empty() {
-    &matches.free[0]
-} else {
-    print_usage(&program, opts);
-    return;
-};
-
-match search(data_file, city) {
-    Ok(pops) => {
-        for pop in pops {
-            println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
+    let mut opts = Options::new();
+    opts.optopt("f", "file", "Choose an input file, instead of using STDIN.", "NAME");
+    opts.optflag("h", "help", "Show this usage message.");
+    ...
+    let data_path = matches.opt_str("f");
+
+    let city = if !matches.free.is_empty() {
+        &matches.free[0]
+    } else {
+        print_usage(&program, opts);
+        return;
+    };
+
+    match search(&data_path, city) {
+        Ok(pops) => {
+            for pop in pops {
+                println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
+            }
         }
+        Err(err) => println!("{}", err)
     }
-    Err(err) => println!("{}", err)
-}
 ...
 ```
 
-In this piece of code, we take `file` (which has the type
-`Option<String>`), and convert it to a type that `search` can use, in
-this case, `&Option<AsRef<Path>>`. To do this, we take a reference of
-file, and map `Path::new` onto it. In this case, `as_ref()` converts
-the `Option<String>` into an `Option<&str>`, and from there, we can
-execute `Path::new` to the content of the optional, and return the
-optional of the new value. Once we have that, it is a simple matter of
-getting the `city` argument and executing `search`.
+We've made the user experience a bit nicer by showing the usage message,
+instead of a panic from an out-of-bounds index, when `city`, the
+remaining free argument, is not present.
 
 Modifying `search` is slightly trickier. The `csv` crate can build a
 parser out of
@@ -2000,6 +1997,8 @@ enum CliError {
 And now for impls on `Display` and `Error`:
 
 ```rust,ignore
+use std::fmt;
+
 impl fmt::Display for CliError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
@@ -2020,13 +2019,13 @@ impl Error for CliError {
         }
     }
 
-    fn cause(&self) -> Option<&error::Error> {
-        match *self {            
+    fn cause(&self) -> Option<&Error> {
+        match *self {
             CliError::Io(ref err) => Some(err),
-            CliError::Parse(ref err) => Some(err),
-            // Our custom error doesn't have an underlying cause, but we could
-            // modify it so that it does.
-            CliError::NotFound() => None,
+            CliError::Csv(ref err) => Some(err),
+            // Our custom error doesn't have an underlying cause,
+            // but we could modify it so that it does.
+            CliError::NotFound => None,
         }
     }
 }
@@ -2122,10 +2121,10 @@ string and add a flag to the Option variable. Once we've done that, Getopts does
 
 ```rust,ignore
 ...
-let mut opts = Options::new();
-opts.optopt("f", "file", "Choose an input file, instead of using STDIN.", "NAME");
-opts.optflag("h", "help", "Show this usage message.");
-opts.optflag("q", "quiet", "Silences errors and warnings.");
+    let mut opts = Options::new();
+    opts.optopt("f", "file", "Choose an input file, instead of using STDIN.", "NAME");
+    opts.optflag("h", "help", "Show this usage message.");
+    opts.optflag("q", "quiet", "Silences errors and warnings.");
 ...
 ```
 
@@ -2133,13 +2132,16 @@ Now we only need to implement our “quiet” functionality. This requires us to
 tweak the case analysis in `main`:
 
 ```rust,ignore
-match search(&args.arg_data_path, &args.arg_city) {
-    Err(CliError::NotFound) if args.flag_quiet => process::exit(1),
-    Err(err) => panic!("{}", err),
-    Ok(pops) => for pop in pops {
-        println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
+use std::process;
+...
+    match search(&data_path, city) {
+        Err(CliError::NotFound) if matches.opt_present("q") => process::exit(1),
+        Err(err) => panic!("{}", err),
+        Ok(pops) => for pop in pops {
+            println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
+        }
     }
-}
+...
 ```
 
 Certainly, we don't want to be quiet if there was an IO error or if the data