Repository: clap-rs/clap
Pull request #1976 (2020-06-18 15:31:57)

Refactor
<!--
If your PR closes some issues, please write `Closes #XXXX`
where `XXXX` is the number of the issue you want to fix.
Each issue goes on it's own line.
-->

Refactor coloring/help/errors facility.

### Improvements

1. _Creation_ of of `clap::Error` is now completely separate from actual _emitting_. It cannot fail anymore.
2. Significant code bloat decrease:
   
   Before
   ``` 
    File  .text     Size Crate
    9.4%  56.8% 289.5KiB clap
    4.8%  28.7% 146.2KiB std
    1.9%  11.6%  59.1KiB [Unknown]
    0.2%   0.9%   4.8KiB indexmap
    0.1%   0.3%   1.6KiB textwrap
    0.0%   0.1%     307B probe
    0.0%   0.0%      41B os_str_bytes
   16.6% 100.0% 509.8KiB .text section size, the file size is 3.0MiB
   ```

   After
   ```
    File  .text     Size Crate
    8.6%  53.7% 262.5KiB clap
    5.0%  31.2% 152.5KiB std
    1.9%  12.1%  59.1KiB [Unknown]
    0.2%   1.0%   4.8KiB indexmap
    0.1%   0.3%   1.6KiB textwrap
    0.0%   0.1%     307B probe
    0.0%   0.0%      41B os_str_bytes
   16.1% 100.0% 489.1KiB .text section size, the file size is 3.0MiB
   ```
3. No more our own unicorn reimplementations of `str::split`. I consider this a huge improvement.

### Changes to public API
1. `App::write_[long_]version` is replaced with render version. It was necessary in order to simplify some code. I found that these methods are [very](https://github.com/Rukenshia/saml2aws-auto/blob/1b483059cc8925e98ecde5fc246b10c6088e0882/src/main.rs#L88) [rarely](https://github.com/lise-henry/crowbook/blob/464b323b4c182bcb736ebfc41d197ac0db117bfd/src/bin/helpers.rs#L271) used, and what those people really needed was `String`.

   Actually, in light of `App::get[_long]_version`, I think we could just remove it. @pksunkara Any objections?
2. `Error::cause` and `Error::info` fields are removed. I've never seen them used in practice, and they are responsible for about `10 KiB` of code bloat.
3. `print_help*` and `write_help*` now return `io::Result` instead of `clap::Result`. There's no point to convert them to clap errors.

### Performance regression

I haven't done proper benchmarking yet, but I expect certain regression in error/help rendering. I suppose the regression is negligible because printing is slow enough on it's own, but I'm open to continue working on improving it.

pksunkara: Something is bugging me about this PR and I can't exactly pinpoint what it is. Which is leading me to procrastinate on reviewing this. 😞 
CreepySkeleton: ping @pksunkara

This PR is essentially a blocker for any other change to the error facility because it spans over almost thousand of lines of code over there.
CreepySkeleton: It's not just about bloat, it's about feature creep. This is a golden opportunity to prune unused and redundant stuff, would be a real shame to miss it. 

Actually, this talk was the reason I removed the fields in the first place - I saw some useless code and wanted to trigger the discussion around it in hope somebody would come with a compelling reason not to remove it. Your argument is basically "let's leave it just in case". Did you ever use them? Can you give an example (some code from a real project, not a hello-world)? 

My arguments:

* `cause` is useless because it's `err.to_string().split("\n\n").next().unwrap()`. Literally. I'm not opposed to having a method encapsulating that - which will be removed by LTO if not used by the way, but I don't see any reason in dedicating a separate field for that and even making use of `std::format!` which isn't exactly lightweight.

  I also don't have a tiniest idea of what it can be used for.
* `info` is useless because I fail to see how it can possibly be used. I have talked to a few people and none of them used these fields and none of them could come up with an idea how-to. While there's obviously sampling bias at play, I think "nobody needs it" is good null hypothesis here.

  I'm not so sure on this one in light of #2016 , but that person didn't supply any particular examples either. I believe this is just a miscommunication of sorts and what they want is actually `.to_string()`. Let's continue the discussion there.
pksunkara: `cause` is not useless. I want to use my CLI in 2 different places (one terminal with colors and one where I don't want the colors (ex: chatroom like described in #2016)), I don't want to run the CLI twice. I would prefer both the error messages to exist in just one run. 
CreepySkeleton: >  If I want to use my CLI in 2 different places (one terminal with colors and one where I don't want the colors

That piece gives me strong impression that you're mistaking `cause` for `to_string` :) How it actually works:

The `Display` impl doesn't do coloring. Never. This is exactly what you're supposed to use when you want no colors. `Colorizer::print` is what you use if you want colors (`Error::exit` uses just that). We'll need to introduce `pub fn Error::print(stderr: bool)`, but I'd like to postpone it for another PR, this one is already hell of a change.

As I noticed earlier, `cause` is just the first sentence from the rendered error (i.e the message without "did you mean" and "hint" stuff). For what do you think you can use this? And as noted, it can be easily emulated via `err.to_string().split("\n\n").next().unwrap()`. 

I accept your reasoning for `info`, I'll put it back.
pickfire: @CreepySkeleton Since we have the luxury to change stuff. Can we enable color by default, it stays hidden for quite a few crates that depends on clap, I have sent pull requests to some of them and they were surprised by this feature.
CreepySkeleton: Colored help `feature = "color"` will  be on by default in 3.0.
pickfire: > Colored help feature = "color" will be on by default in 3.0.

Yes, the feature is on by default. IIRC even in 2.0 but I mean `AppSettings::ColoredHelp`, not the cargo feature but the clap setting to show colored help by default. Looking at `clap_derive` `basic` example, `cargo run --example basic -- -h` does not show any color.

By the way, I have a comment above that you missed.
CreepySkeleton: @pickfire Could you please create a separate issue about coloring? For the record, I agree. The `Auto, Never, Always` settings set is enough.
pksunkara: Your tests are failing because new stable is released. I fixed the UI tests in #2114 
pksunkara: Once squashed, you can do `bors r=pksunkara` to merge. 👍 

Patch:
diff --git a/src/build/app/mod.rs b/src/build/app/mod.rs
index dc8e47bd..459f9fd0 100644
--- a/src/build/app/mod.rs
+++ b/src/build/app/mod.rs
@@ -1665,18 +1665,15 @@ impl<'help> App<'help> {
     /// app.print_help();
     /// ```
     /// [`io::stdout()`]: https://doc.rust-lang.org/std/io/fn.stdout.html
-    /// [`BufWriter`]: https://doc.rust-lang.org/std/io/struct.BufWriter.html
     /// [`-h` (short)]: ./struct.Arg.html#method.about
     /// [`--help` (long)]: ./struct.Arg.html#method.long_about
-    pub fn print_help(&mut self) -> ClapResult<()> {
+    pub fn print_help(&mut self) -> io::Result<()> {
         self._build();
 
         let p = Parser::new(self);
         let mut c = Colorizer::new(false, p.color_help());
-
         Help::new(HelpWriter::Buffer(&mut c), &p, false).write_help()?;
-
-        Ok(c.print()?)
+        c.print()
     }
 
     /// Prints the full help message to [`io::stdout()`] using a [`BufWriter`] using the same
@@ -1696,15 +1693,13 @@ impl<'help> App<'help> {
     /// [`BufWriter`]: https://doc.rust-lang.org/std/io/struct.BufWriter.html
     /// [`-h` (short)]: ./struct.Arg.html#method.about
     /// [`--help` (long)]: ./struct.Arg.html#method.long_about
-    pub fn print_long_help(&mut self) -> ClapResult<()> {
+    pub fn print_long_help(&mut self) -> io::Result<()> {
         self._build();
 
         let p = Parser::new(self);
         let mut c = Colorizer::new(false, p.color_help());
-
         Help::new(HelpWriter::Buffer(&mut c), &p, true).write_help()?;
-
-        Ok(c.print()?)
+        c.print()
     }
 
     /// Writes the full help message to the user to a [`io::Write`] object in the same method as if
@@ -1725,11 +1720,12 @@ impl<'help> App<'help> {
     /// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
     /// [`-h` (short)]: ./struct.Arg.html#method.about
     /// [`--help` (long)]: ./struct.Arg.html#method.long_about
-    pub fn write_help<W: Write>(&mut self, w: &mut W) -> ClapResult<()> {
+    pub fn write_help<W: Write>(&mut self, w: &mut W) -> io::Result<()> {
         self._build();
 
         let p = Parser::new(self);
-        Help::new(HelpWriter::Normal(w), &p, false).write_help()
+        Help::new(HelpWriter::Normal(w), &p, false).write_help()?;
+        w.flush()
     }
 
     /// Writes the full help message to the user to a [`io::Write`] object in the same method as if
@@ -1750,53 +1746,61 @@ impl<'help> App<'help> {
     /// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
     /// [`-h` (short)]: ./struct.Arg.html#method.about
     /// [`--help` (long)]: ./struct.Arg.html#method.long_about
-    pub fn write_long_help<W: Write>(&mut self, w: &mut W) -> ClapResult<()> {
+    pub fn write_long_help<W: Write>(&mut self, w: &mut W) -> io::Result<()> {
         self._build();
 
         let p = Parser::new(self);
-        Help::new(HelpWriter::Normal(w), &p, true).write_help()
+        Help::new(HelpWriter::Normal(w), &p, true).write_help()?;
+        w.flush()
     }
 
-    /// Writes the version message to the user to a [`io::Write`] object as if the user ran `-V`.
+    /// Returns the version message rendered as if the user ran `-V`.
     ///
     /// **NOTE:** clap has the ability to distinguish between "short" and "long" version messages
     /// depending on if the user ran [`-V` (short)] or [`--version` (long)].
     ///
-    /// # Examples
+    /// ### Coloring
+    ///
+    /// This function does not try to color the message nor it inserts any [ANSI escape codes].
+    ///
+    /// ### Examples
     ///
     /// ```rust
     /// # use clap::App;
     /// use std::io;
-    /// let mut app = App::new("myprog");
-    /// let mut out = io::stdout();
-    /// app.write_version(&mut out).expect("failed to write to stdout");
+    /// let app = App::new("myprog");
+    /// println!("{}", app.render_version());
     /// ```
     /// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
     /// [`-V` (short)]: ./struct.App.html#method.version
     /// [`--version` (long)]: ./struct.App.html#method.long_version
-    pub fn write_version<W: Write>(&self, w: &mut W) -> ClapResult<()> {
-        self._write_version(w, false).map_err(From::from)
+    /// [ANSI escape codes]: https://en.wikipedia.org/wiki/ANSI_escape_code
+    pub fn render_version(&self) -> String {
+        self._render_version(false)
     }
 
-    /// Writes the version message to the user to a [`io::Write`] object.
+    /// Returns the version message rendered as if the user ran `--version`.
     ///
     /// **NOTE:** clap has the ability to distinguish between "short" and "long" version messages
     /// depending on if the user ran [`-V` (short)] or [`--version` (long)].
     ///
-    /// # Examples
+    /// ### Coloring
+    ///
+    /// This function does not try to color the message nor it inserts any [ANSI escape codes].
+    ///
+    /// ### Examples
     ///
     /// ```rust
     /// # use clap::App;
     /// use std::io;
-    /// let mut app = App::new("myprog");
-    /// let mut out = io::stdout();
-    /// app.write_long_version(&mut out).expect("failed to write to stdout");
+    /// let app = App::new("myprog");
+    /// println!("{}", app.render_long_version());
     /// ```
     /// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
     /// [`-V` (short)]: ./struct.App.html#method.version
     /// [`--version` (long)]: ./struct.App.html#method.long_version
-    pub fn write_long_version<W: Write>(&self, w: &mut W) -> ClapResult<()> {
-        self._write_version(w, true).map_err(From::from)
+    pub fn render_long_version(&self) -> String {
+        self._render_version(true)
     }
 
     /// @TODO-v3-alpha @docs @p2: write docs
@@ -2111,7 +2115,7 @@ impl<'help> App<'help> {
         let mut matcher = ArgMatcher::default();
 
         // If there are global arguments, or settings we need to propagate them down to subcommands
-        // before parsing incase we run into a subcommand
+        // before parsing in case we run into a subcommand
         if !self.settings.is_set(AppSettings::Built) {
             self._build();
         }
@@ -2150,12 +2154,9 @@ impl<'help> App<'help> {
         for a in self.args.args.iter_mut() {
             // Fill in the groups
             for g in &a.groups {
-                let mut found = false;
                 if let Some(ag) = self.groups.iter_mut().find(|grp| grp.id == *g) {
                     ag.args.push(a.id.clone());
-                    found = true;
-                }
-                if !found {
+                } else {
                     let mut ag = ArgGroup::with_id(g.clone());
                     ag.args.push(a.id.clone());
                     self.groups.push(ag);
@@ -2396,8 +2397,8 @@ impl<'help> App<'help> {
         }
     }
 
-    pub(crate) fn _write_version<W: Write>(&self, w: &mut W, use_long: bool) -> io::Result<()> {
-        debug!("App::_write_version");
+    pub(crate) fn _render_version(&self, use_long: bool) -> String {
+        debug!("App::_render_version");
 
         let ver = if use_long {
             self.long_version
@@ -2409,12 +2410,12 @@ impl<'help> App<'help> {
         if let Some(bn) = self.bin_name.as_ref() {
             if bn.contains(' ') {
                 // In case we're dealing with subcommands i.e. git mv is translated to git-mv
-                writeln!(w, "{} {}", bn.replace(" ", "-"), ver)
+                format!("{} {}\n", bn.replace(" ", "-"), ver)
             } else {
-                writeln!(w, "{} {}", &self.name[..], ver)
+                format!("{} {}\n", &self.name[..], ver)
             }
         } else {
-            writeln!(w, "{} {}", &self.name[..], ver)
+            format!("{} {}\n", &self.name[..], ver)
         }
     }
 
diff --git a/src/output/fmt.rs b/src/output/fmt.rs
index a4a7d833..e385defe 100644
--- a/src/output/fmt.rs
+++ b/src/output/fmt.rs
@@ -1,10 +1,11 @@
-use crate::util::termcolor::{Buffer, BufferWriter, ColorChoice};
+#[cfg(not(feature = "color"))]
+use crate::util::termcolor::{Color, ColorChoice};
 #[cfg(feature = "color")]
-use crate::util::termcolor::{Color, ColorSpec, WriteColor};
+use termcolor::{Color, ColorChoice};
 
 use std::{
-    fmt::{self, Debug, Formatter},
-    io::{Result, Write},
+    fmt::{self, Display, Formatter},
+    io::{self, Write},
 };
 
 #[cfg(feature = "color")]
@@ -20,103 +21,102 @@ fn is_a_tty(stderr: bool) -> bool {
     atty::is(stream)
 }
 
-#[cfg(not(feature = "color"))]
-fn is_a_tty(_: bool) -> bool {
-    debug!("is_a_tty");
-    false
-}
-
+#[derive(Debug)]
 pub(crate) struct Colorizer {
-    writer: BufferWriter,
-    buffer: Buffer,
+    use_stderr: bool,
+    color_when: ColorChoice,
+    pieces: Vec<(String, Option<Color>)>,
 }
 
-impl Debug for Colorizer {
-    #[cfg(feature = "color")]
-    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
-        write!(f, "{}", String::from_utf8_lossy(self.buffer.as_slice()))
+impl Colorizer {
+    #[inline]
+    pub(crate) fn new(use_stderr: bool, color_when: ColorChoice) -> Self {
+        Colorizer {
+            use_stderr,
+            color_when,
+            pieces: vec![],
+        }
     }
 
-    #[cfg(not(feature = "color"))]
-    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
-        write!(f, "{}", String::from_utf8_lossy(&self.buffer))
+    #[inline]
+    pub(crate) fn good(&mut self, msg: impl Into<String>) {
+        self.pieces.push((msg.into(), Some(Color::Green)));
+    }
+
+    #[inline]
+    pub(crate) fn warning(&mut self, msg: impl Into<String>) {
+        self.pieces.push((msg.into(), Some(Color::Yellow)));
+    }
+
+    #[inline]
+    pub(crate) fn error(&mut self, msg: impl Into<String>) {
+        self.pieces.push((msg.into(), Some(Color::Red)));
+    }
+
+    #[inline]
+    pub(crate) fn none(&mut self, msg: impl Into<String>) {
+        self.pieces.push((msg.into(), None));
     }
 }
 
+/// Printing methods.
 impl Colorizer {
-    pub(crate) fn new(use_stderr: bool, when: ColorChoice) -> Self {
-        let checked_when = if is_a_tty(use_stderr) {
-            when
+    #[cfg(feature = "color")]
+    pub(crate) fn print(&self) -> io::Result<()> {
+        use termcolor::{BufferWriter, ColorSpec, WriteColor};
+
+        let color_when = if is_a_tty(self.use_stderr) {
+            self.color_when
         } else {
             ColorChoice::Never
         };
 
-        let writer = if use_stderr {
-            BufferWriter::stderr(checked_when)
+        let writer = if self.use_stderr {
+            BufferWriter::stderr(color_when)
         } else {
-            BufferWriter::stdout(checked_when)
+            BufferWriter::stdout(color_when)
         };
 
-        let buffer = writer.buffer();
+        let mut buffer = writer.buffer();
 
-        Self { writer, buffer }
-    }
+        for piece in &self.pieces {
+            let mut color = ColorSpec::new();
+            color.set_fg(piece.1);
+            if piece.1 == Some(Color::Red) {
+                color.set_bold(true);
+            }
 
-    pub(crate) fn print(&self) -> Result<()> {
-        self.writer.print(&self.buffer)
-    }
+            buffer.set_color(&color)?;
+            buffer.write_all(piece.0.as_bytes())?;
+            buffer.reset()?;
+        }
 
-    #[cfg(feature = "color")]
-    pub(crate) fn good(&mut self, msg: &str) -> Result<()> {
-        self.buffer
-            .set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;
-        self.write_all(msg.as_bytes())?;
-        self.buffer.reset()
+        writer.print(&buffer)
     }
 
     #[cfg(not(feature = "color"))]
-    pub(crate) fn good(&mut self, msg: &str) -> Result<()> {
-        self.none(msg)
-    }
-
-    #[cfg(feature = "color")]
-    pub(crate) fn warning(&mut self, msg: &str) -> Result<()> {
-        self.buffer
-            .set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))?;
-        self.write_all(msg.as_bytes())?;
-        self.buffer.reset()
-    }
-
-    #[cfg(not(feature = "color"))]
-    pub(crate) fn warning(&mut self, msg: &str) -> Result<()> {
-        self.none(msg)
-    }
-
-    #[cfg(feature = "color")]
-    pub(crate) fn error(&mut self, msg: &str) -> Result<()> {
-        self.buffer
-            .set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true))?;
-        self.write_all(msg.as_bytes())?;
-        self.buffer.reset()
-    }
-
-    #[cfg(not(feature = "color"))]
-    pub(crate) fn error(&mut self, msg: &str) -> Result<()> {
-        self.none(msg)
-    }
-
-    pub(crate) fn none(&mut self, msg: &str) -> Result<()> {
-        self.write_all(msg.as_bytes())?;
-        Ok(())
+    pub(crate) fn print(&self) -> io::Result<()> {
+        // [e]println can't be used here because it panics
+        // if something went wrong. We don't want that.
+        if self.use_stderr {
+            let stderr = std::io::stderr();
+            let mut stderr = stderr.lock();
+            write!(stderr, "{}", self)
+        } else {
+            let stdout = std::io::stdout();
+            let mut stdout = stdout.lock();
+            write!(stdout, "{}", self)
+        }
     }
 }
 
-impl Write for Colorizer {
-    fn write(&mut self, buf: &[u8]) -> Result<usize> {
-        self.buffer.write(buf)
-    }
+/// Color-unaware printing. Never uses coloring.
+impl Display for Colorizer {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        for piece in &self.pieces {
+            Display::fmt(&piece.0, f)?;
+        }
 
-    fn flush(&mut self) -> Result<()> {
-        self.buffer.flush()
+        Ok(())
     }
 }
diff --git a/src/output/help.rs b/src/output/help.rs
index ed11da1a..0ab1082a 100644
--- a/src/output/help.rs
+++ b/src/output/help.rs
@@ -3,7 +3,7 @@ use std::{
     borrow::Cow,
     cmp,
     collections::BTreeMap,
-    io::{self, Cursor, Read, Write},
+    io::{self, Write},
     usize,
 };
 
@@ -11,10 +11,7 @@ use std::{
 use crate::{
     build::{App, AppSettings, Arg, ArgSettings},
     output::{fmt::Colorizer, Usage},
-    parse::{
-        errors::{Error, Result as ClapResult},
-        Parser,
-    },
+    parse::Parser,
     util::VecMap,
     INTERNAL_ERROR_MSG,
 };
@@ -42,22 +39,6 @@ pub(crate) enum HelpWriter<'writer> {
     Buffer(&'writer mut Colorizer),
 }
 
-impl<'writer> Write for HelpWriter<'writer> {
-    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        match self {
-            HelpWriter::Normal(n) => n.write(buf),
-            HelpWriter::Buffer(c) => c.write(buf),
-        }
-    }
-
-    fn flush(&mut self) -> io::Result<()> {
-        match self {
-            HelpWriter::Normal(n) => n.flush(),
-            HelpWriter::Buffer(c) => c.flush(),
-        }
-    }
-}
-
 /// `clap` Help Writer.
 ///
 /// Wraps a writer stream providing different methods to generate help for `clap` objects.
@@ -122,11 +103,11 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
     }
 
     /// Writes the parser help to the wrapped stream.
-    pub(crate) fn write_help(&mut self) -> ClapResult<()> {
+    pub(crate) fn write_help(&mut self) -> io::Result<()> {
         debug!("Help::write_help");
 
         if let Some(h) = self.parser.app.help_str {
-            self.none(h).map_err(Error::from)?;
+            self.none(h)?;
         } else if let Some(tmpl) = self.parser.app.template {
             self.write_templated_help(tmpl)?;
         } else {
@@ -151,8 +132,11 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
 macro_rules! write_method {
     ($_self:ident, $msg:ident, $meth:ident) => {
         match &mut $_self.writer {
-            HelpWriter::Buffer(c) => c.$meth($msg),
-            HelpWriter::Normal(w) => write!(w, "{}", $msg),
+            HelpWriter::Buffer(c) => {
+                c.$meth(($msg).into());
+                Ok(())
+            }
+            HelpWriter::Normal(w) => w.write_all($msg.as_ref()),
         }
     };
 }
@@ -168,15 +152,15 @@ macro_rules! write_nspaces {
 
 // Methods to write Arg help.
 impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
-    fn good(&mut self, msg: &str) -> io::Result<()> {
+    fn good<T: Into<String> + AsRef<[u8]>>(&mut self, msg: T) -> io::Result<()> {
         write_method!(self, msg, good)
     }
 
-    fn warning(&mut self, msg: &str) -> io::Result<()> {
+    fn warning<T: Into<String> + AsRef<[u8]>>(&mut self, msg: T) -> io::Result<()> {
         write_method!(self, msg, warning)
     }
 
-    fn none(&mut self, msg: &str) -> io::Result<()> {
+    fn none<T: Into<String> + AsRef<[u8]>>(&mut self, msg: T) -> io::Result<()> {
         write_method!(self, msg, none)
     }
 
@@ -693,17 +677,15 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
 impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
     /// Writes help for all arguments (options, flags, args, subcommands)
     /// including titles of a Parser Object to the wrapped stream.
-    pub(crate) fn write_all_args(&mut self) -> ClapResult<()> {
+    pub(crate) fn write_all_args(&mut self) -> io::Result<()> {
         debug!("Help::write_all_args");
         let flags = self.parser.has_flags();
-        // FIXME: Strange filter/count vs fold... https://github.com/rust-lang/rust/issues/33038
-        let pos = self.parser.app.get_positionals().fold(0, |acc, arg| {
-            if should_show_arg(self.use_long, arg) {
-                acc + 1
-            } else {
-                acc
-            }
-        }) > 0;
+        let pos = self
+            .parser
+            .app
+            .get_positionals()
+            .filter(|arg| should_show_arg(self.use_long, arg))
+            .any(|_| true);
         let opts = self
             .parser
             .app
@@ -850,7 +832,7 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
         }
         if let Some(bn) = self.parser.app.bin_name.as_ref() {
             if bn.contains(' ') {
-                // Incase we're dealing with subcommands i.e. git mv is translated to git-mv
+                // In case we're dealing with subcommands i.e. git mv is translated to git-mv
                 self.good(&bn.replace(" ", "-"))?
             } else {
                 write_name!();
@@ -862,103 +844,6 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
     }
 }
 
-/// Possible results for a copying function that stops when a given
-/// byte was found.
-enum CopyUntilResult {
-    DelimiterFound(usize),
-    DelimiterNotFound(usize),
-    ReaderEmpty,
-    ReadError(io::Error),
-    WriteError(io::Error),
-}
-
-/// Copies the contents of a reader into a writer until a delimiter byte is found.
-/// On success, the total number of bytes that were
-/// copied from reader to writer is returned.
-fn copy_until<R: Read, W: Write>(r: &mut R, w: &mut W, delimiter_byte: u8) -> CopyUntilResult {
-    debug!("copy_until");
-
-    let mut count = 0;
-    for wb in r.bytes() {
-        match wb {
-            Ok(b) => {
-                if b == delimiter_byte {
-                    return CopyUntilResult::DelimiterFound(count);
-                }
-                match w.write(&[b]) {
-                    Ok(c) => count += c,
-                    Err(e) => return CopyUntilResult::WriteError(e),
-                }
-            }
-            Err(e) => return CopyUntilResult::ReadError(e),
-        }
-    }
-    if count > 0 {
-        CopyUntilResult::DelimiterNotFound(count)
-    } else {
-        CopyUntilResult::ReaderEmpty
-    }
-}
-
-/// Copies the contents of a reader into a writer until a {tag} is found,
-/// copying the tag content to a buffer and returning its size.
-/// In addition to errors, there are three possible outputs:
-///   - `None`: The reader was consumed.
-///   - `Some(Ok(0))`: No tag was captured but the reader still contains data.
-///   - `Some(Ok(length>0))`: a tag with `length` was captured to the `tag_buffer`.
-fn copy_and_capture<R: Read, W: Write>(
-    r: &mut R,
-    w: &mut W,
-    tag_buffer: &mut Cursor<Vec<u8>>,
-) -> Option<io::Result<usize>> {
-    use self::CopyUntilResult::*;
-    debug!("copy_and_capture");
-
-    // Find the opening byte.
-    match copy_until(r, w, b'{') {
-        // The end of the reader was reached without finding the opening tag.
-        // (either with or without having copied data to the writer)
-        // Return None indicating that we are done.
-        ReaderEmpty | DelimiterNotFound(_) => None,
-
-        // Something went wrong.
-        ReadError(e) | WriteError(e) => Some(Err(e)),
-
-        // The opening byte was found.
-        // (either with or without having copied data to the writer)
-        DelimiterFound(_) => {
-            // Lets reset the buffer first and find out how long it is.
-            tag_buffer.set_position(0);
-            let buffer_size = tag_buffer.get_ref().len();
-
-            // Find the closing byte,limiting the reader to the length of the buffer.
-            let mut rb = r.take(buffer_size as u64);
-            match copy_until(&mut rb, tag_buffer, b'}') {
-                // We were already at the end of the reader.
-                // Return None indicating that we are done.
-                ReaderEmpty => None,
-
-                // The closing tag was found.
-                // Return the tag_length.
-                DelimiterFound(tag_length) => Some(Ok(tag_length)),
-
-                // The end of the reader was found without finding the closing tag.
-                // Write the opening byte and captured text to the writer.
-                // Return 0 indicating that nothing was captured but the reader still contains data.
-                DelimiterNotFound(not_tag_length) => match w.write(b"{") {
-                    Err(e) => Some(Err(e)),
-                    _ => match w.write(&tag_buffer.get_ref()[0..not_tag_length]) {
-                        Err(e) => Some(Err(e)),
-                        _ => Some(Ok(0)),
-                    },
-                },
-
-                ReadError(e) | WriteError(e) => Some(Err(e)),
-            }
-        }
-    }
-}
-
 // Methods to write Parser help using templates.
 impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
     /// Write help to stream for the parser in the format defined by the template.
@@ -966,10 +851,8 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
     /// For details about the template language see [`App::help_template`].
     ///
     /// [`App::help_template`]: ./struct.App.html#method.help_template
-    fn write_templated_help(&mut self, template: &str) -> ClapResult<()> {
+    fn write_templated_help(&mut self, template: &str) -> io::Result<()> {
         debug!("Help::write_templated_help");
-        let mut tmplr = Cursor::new(&template);
-        let mut tag_buf = Cursor::new(vec![0u8; 20]);
 
         // The strategy is to copy the template from the reader to wrapped stream
         // until a tag is found. Depending on its value, the appropriate content is copied
@@ -977,127 +860,140 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
         // The copy from template is then resumed, repeating this sequence until reading
         // the complete template.
 
-        loop {
-            let tag_length = match copy_and_capture(&mut tmplr, &mut self.writer, &mut tag_buf) {
-                None => return Ok(()),
-                Some(Err(e)) => return Err(Error::from(e)),
-                Some(Ok(val)) if val > 0 => val,
-                _ => continue,
-            };
-
-            debug!(
-                "Help::write_template_help:iter: tag_buf={}",
-                String::from_utf8_lossy(&tag_buf.get_ref()[0..tag_length])
-            );
-            match &tag_buf.get_ref()[0..tag_length] {
-                b"?" => {
-                    self.none("Could not decode tag name")?;
-                }
-                b"bin" => {
-                    self.write_bin_name()?;
+        macro_rules! tags {
+            (
+                match $part:ident {
+                    $( $tag:expr => $action:stmt )*
                 }
-                b"version" => {
-                    if let Some(output) = self.parser.app.version {
-                        self.none(output)?;
+            ) => {
+                match $part {
+                    $(
+                        part if part.starts_with(concat!($tag, "}")) => {
+                            $action
+                            let rest = &part[$tag.len()+1..];
+                            self.none(rest)?;
+                        }
+                    )*
+
+                    // Unknown tag, write it back.
+                    part => {
+                        self.none("{")?;
+                        self.none(part)?;
                     }
                 }
-                b"author" => {
-                    if let Some(output) = self.parser.app.author {
-                        self.none(&wrap_help(output, self.term_w))?;
+            };
+        }
+
+        let mut parts = template.split('{');
+        if let Some(first) = parts.next() {
+            self.none(first)?;
+        }
+
+        for part in parts {
+            tags! {
+                match part {
+                    "bin" => {
+                        self.write_bin_name()?;
                     }
-                }
-                b"author-with-newline" => {
-                    if let Some(output) = self.parser.app.author {
-                        self.none(&wrap_help(output, self.term_w))?;
-                        self.none("\n")?;
+                    "version" => {
+                        if let Some(s) = self.parser.app.version {
+                            self.none(s)?;
+                        }
                     }
-                }
-                b"about" => {
-                    let about = if self.use_long {
-                        self.parser.app.long_about.or(self.parser.app.about)
-                    } else {
-                        self.parser.app.about
-                    };
-                    if let Some(output) = about {
-                        self.none(&wrap_help(output, self.term_w))?;
+                    "author" => {
+                        if let Some(s) = self.parser.app.author {
+                            self.none(&wrap_help(s, self.term_w))?;
+                        }
                     }
-                }
-                b"about-with-newline" => {
-                    let about = if self.use_long {
-                        self.parser.app.long_about.or(self.parser.app.about)
-                    } else {
-                        self.parser.app.about
-                    };
-                    if let Some(output) = about {
-                        self.none(&wrap_help(output, self.term_w))?;
-                        self.none("\n")?;
+                    "author-with-newline" => {
+                        if let Some(s) = self.parser.app.author {
+                            self.none(&wrap_help(s, self.term_w))?;
+                            self.none("\n")?;
+                        }
                     }
-                }
-                b"usage" => {
-                    self.none(&Usage::new(self.parser).create_usage_no_title(&[]))?;
-                }
-                b"all-args" => {
-                    self.write_all_args()?;
-                }
-                b"unified" => {
-                    let opts_flags = self
-                        .parser
-                        .app
-                        .args
-                        .args
-                        .iter()
-                        .filter(|a| a.has_switch())
-                        .collect::<Vec<_>>();
-                    self.write_args(&opts_flags)?;
-                }
-                b"flags" => {
-                    self.write_args(&self.parser.app.get_flags_no_heading().collect::<Vec<_>>())?;
-                }
-                b"options" => {
-                    self.write_args(&self.parser.app.get_opts_no_heading().collect::<Vec<_>>())?;
-                }
-                b"positionals" => {
-                    self.write_args(&self.parser.app.get_positionals().collect::<Vec<_>>())?;
-                }
-                b"subcommands" => {
-                    self.write_subcommands(self.parser.app)?;
-                }
-                b"after-help" => {
-                    let after_help = if self.use_long {
-                        self.parser
-                            .app
-                            .after_long_help
-                            .or(self.parser.app.after_help)
-                    } else {
-                        self.parser.app.after_help
-                    };
-                    if let Some(output) = after_help {
-                        self.none("\n\n")?;
-                        self.write_before_after_help(output)?;
+                    "about" => {
+                        let about = if self.use_long {
+                            self.parser.app.long_about.or(self.parser.app.about)
+                        } else {
+                            self.parser.app.about
+                        };
+                        if let Some(output) = about {
+                            self.none(wrap_help(output, self.term_w))?;
+                        }
                     }
-                }
-                b"before-help" => {
-                    let before_help = if self.use_long {
-                        self.parser
+                    "about-with-newline" => {
+                        let about = if self.use_long {
+                            self.parser.app.long_about.or(self.parser.app.about)
+                        } else {
+                            self.parser.app.about
+                        };
+                        if let Some(output) = about {
+                            self.none(wrap_help(output, self.term_w))?;
+                            self.none("\n")?;
+                        }
+                    }
+                    "usage" => {
+                        self.none(Usage::new(self.parser).create_usage_no_title(&[]))?;
+                    }
+                    "all-args" => {
+                        self.write_all_args()?;
+                    }
+                    "unified" => {
+                        let opts_flags = self
+                            .parser
                             .app
-                            .before_long_help
-                            .or(self.parser.app.before_help)
-                    } else {
-                        self.parser.app.before_help
-                    };
-                    if let Some(output) = before_help {
-                        self.write_before_after_help(output)?;
-                        self.none("\n\n")?;
+                            .args
+                            .args
+                            .iter()
+                            .filter(|a| a.has_switch())
+                            .collect::<Vec<_>>();
+                        self.write_args(&opts_flags)?;
+                    }
+                    "flags" => {
+                        self.write_args(&self.parser.app.get_flags_no_heading().collect::<Vec<_>>())?;
+                    }
+                    "options" => {
+                        self.write_args(&self.parser.app.get_opts_no_heading().collect::<Vec<_>>())?;
+                    }
+                    "positionals" => {
+                        self.write_args(&self.parser.app.get_positionals().collect::<Vec<_>>())?;
+                    }
+                    "subcommands" => {
+                        self.write_subcommands(self.parser.app)?;
+                    }
+                    "after-help" => {
+                        let after_help = if self.use_long {
+                            self.parser
+                                .app
+                                .after_long_help
+                                .or(self.parser.app.after_help)
+                        } else {
+                            self.parser.app.after_help
+                        };
+                        if let Some(output) = after_help {
+                            self.none("\n\n")?;
+                            self.write_before_after_help(output)?;
+                        }
+                    }
+                    "before-help" => {
+                        let before_help = if self.use_long {
+                            self.parser
+                                .app
+                                .before_long_help
+                                .or(self.parser.app.before_help)
+                        } else {
+                            self.parser.app.before_help
+                        };
+                        if let Some(output) = before_help {
+                            self.write_before_after_help(output)?;
+                            self.none("\n\n")?;
+                        }
                     }
-                }
-                // Unknown tag, write it back.
-                r => {
-                    self.none("{")?;
-                    self.writer.write_all(r)?;
-                    self.none("}")?;
                 }
             }
         }
+
+        Ok(())
     }
 }
 
diff --git a/src/output/usage.rs b/src/output/usage.rs
index 137c0e54..d3213cb1 100644
--- a/src/output/usage.rs
+++ b/src/output/usage.rs
@@ -1,5 +1,5 @@
 // std
-use std::collections::{BTreeMap, VecDeque};
+use std::collections::BTreeMap;
 
 // Internal
 use crate::{
@@ -241,9 +241,12 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> {
                 count
             );
         }
+
         if !self.p.is_set(AS::DontCollapseArgsInUsage) && count > 1 {
             debug!("Usage::get_args_tag:iter: More than one, returning [ARGS]");
-            return None; // [ARGS]
+
+            // [ARGS]
+            None
         } else if count == 1 && incl_reqs {
             let pos = self
                 .p
@@ -255,21 +258,23 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> {
                         && !pos.is_set(ArgSettings::Last)
                 })
                 .expect(INTERNAL_ERROR_MSG);
+
             debug!(
                 "Usage::get_args_tag:iter: Exactly one, returning '{}'",
                 pos.name
             );
-            return Some(format!(
+
+            Some(format!(
                 " [{}]{}",
                 pos.name_no_brackets(),
                 pos.multiple_str()
-            ));
+            ))
         } else if self.p.is_set(AS::DontCollapseArgsInUsage)
             && self.p.has_positionals()
             && incl_reqs
         {
             debug!("Usage::get_args_tag:iter: Don't collapse returning all");
-            return Some(
+            Some(
                 self.p
                     .app
                     .get_positionals()
@@ -279,7 +284,7 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> {
                     .map(|pos| format!(" [{}]{}", pos.name_no_brackets(), pos.multiple_str()))
                     .collect::<Vec<_>>()
                     .join(""),
-            );
+            )
         } else if !incl_reqs {
             debug!("Usage::get_args_tag:iter: incl_reqs=false, building secondary usage string");
             let highest_req_pos = self
@@ -295,7 +300,7 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> {
                 })
                 .max()
                 .unwrap_or_else(|| Some(self.p.app.get_positionals().count() as u64));
-            return Some(
+            Some(
                 self.p
                     .app
                     .get_positionals()
@@ -306,9 +311,10 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> {
                     .map(|pos| format!(" [{}]{}", pos.name_no_brackets(), pos.multiple_str()))
                     .collect::<Vec<_>>()
                     .join(""),
-            );
+            )
+        } else {
+            Some("".into())
         }
-        Some("".into())
     }
 
     // Determines if we need the `[FLAGS]` tag in the usage string
@@ -316,11 +322,14 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> {
         debug!("Usage::needs_flags_tag");
         'outer: for f in self.p.app.get_flags_no_heading() {
             debug!("Usage::needs_flags_tag:iter: f={}", f.name);
-            if let Some(l) = f.long {
-                if l == "help" || l == "version" {
-                    // Don't print `[FLAGS]` just for help or version
-                    continue;
-                }
+
+            // Don't print `[FLAGS]` just for help or version
+            if f.long == Some("help") || f.long == Some("version") {
+                continue;
+            }
+
+            if f.is_set(ArgSettings::Hidden) {
+                continue;
             }
             for grp_s in self.p.app.groups_for_arg(&f.id) {
                 debug!("Usage::needs_flags_tag:iter:iter: grp_s={:?}", grp_s);
@@ -335,9 +344,7 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> {
                     continue 'outer;
                 }
             }
-            if f.is_set(ArgSettings::Hidden) {
-                continue;
-            }
+
             debug!("Usage::needs_flags_tag:iter: [FLAGS] required");
             return true;
         }
@@ -347,7 +354,7 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> {
     }
 
     // Returns the required args in usage string form by fully unrolling all groups
-    // `incl_last`: should we incldue args that are Arg::Last? (i.e. `prog [foo] -- [last]). We
+    // `incl_last`: should we include args that are Arg::Last? (i.e. `prog [foo] -- [last]). We
     // can't do that for required usages being built for subcommands because it would look like:
     // `prog [foo] -- [last] <subcommand>` which is totally wrong.
     pub(crate) fn get_required_usage_from(
@@ -355,14 +362,14 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> {
         incls: &[Id],
         matcher: Option<&ArgMatcher>,
         incl_last: bool,
-    ) -> VecDeque<String> {
+    ) -> Vec<String> {
         debug!(
             "Usage::get_required_usage_from: incls={:?}, matcher={:?}, incl_last={:?}",
             incls,
             matcher.is_some(),
             incl_last
         );
-        let mut ret_val = VecDeque::new();
+        let mut ret_val = Vec::new();
 
         let mut unrolled_reqs = vec![];
 
@@ -405,7 +412,7 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> {
         for p in pmap.values() {
             debug!("Usage::get_required_usage_from:iter:{:?}", p.id);
             if args_in_groups.is_empty() || !args_in_groups.contains(&p.id) {
-                ret_val.push_back(p.to_string());
+                ret_val.push(p.to_string());
             }
         }
         for a in unrolled_reqs
@@ -423,7 +430,7 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> {
                 .find(&a)
                 .map(ToString::to_string)
                 .expect(INTERNAL_ERROR_MSG);
-            ret_val.push_back(arg);
+            ret_val.push(arg);
         }
         let mut g_vec: Vec<String> = vec![];
         for g in unrolled_reqs
@@ -449,7 +456,7 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> {
             }
         }
         for g in g_vec {
-            ret_val.push_back(g);
+            ret_val.push(g);
         }
 
         debug!("Usage::get_required_usage_from: ret_val={:?}", ret_val);
diff --git a/src/parse/errors.rs b/src/parse/errors.rs
index fa96ac57..da5ae641 100644
--- a/src/parse/errors.rs
+++ b/src/parse/errors.rs
@@ -1,6 +1,5 @@
 // Std
 use std::{
-    collections::VecDeque,
     convert::From,
     fmt::{self, Debug, Display, Formatter},
     io,
@@ -9,7 +8,7 @@ use std::{
 
 // Internal
 use crate::{
-    build::{Arg, ArgGroup},
+    build::Arg,
     output::fmt::Colorizer,
     parse::features::suggestions,
     util::{safe_exit, termcolor::ColorChoice},
@@ -375,39 +374,36 @@ pub enum ErrorKind {
 /// Command Line Argument Parser Error
 #[derive(Debug)]
 pub struct Error {
-    /// The cause of the error
-    pub cause: String,
     /// Formatted error message, enhancing the cause message with extra information
     pub(crate) message: Colorizer,
     /// The type of error
     pub kind: ErrorKind,
-    /// Any additional information passed along, such as the argument name that caused the error
-    pub info: Option<Vec<String>>,
+    /// Additional information depending on the error kind, like values and argument names.
+    /// Useful when you want to render an error of your own.
+    pub info: Vec<String>,
 }
 
 impl Display for Error {
     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
-        self.message.fmt(f)
+        Display::fmt(&self.message, f)
     }
 }
 
-fn start_error(c: &mut Colorizer, msg: &str) -> io::Result<()> {
-    c.error("error:")?;
-    c.none(" ")?;
-    c.none(msg)
+fn start_error(c: &mut Colorizer, msg: impl Into<String>) {
+    c.error("error:");
+    c.none(" ");
+    c.none(msg);
 }
 
-fn put_usage<U>(c: &mut Colorizer, usage: U) -> io::Result<()>
-where
-    U: Display,
-{
-    c.none(&format!("\n\n{}", usage))
+fn put_usage(c: &mut Colorizer, usage: impl Into<String>) {
+    c.none("\n\n");
+    c.none(usage);
 }
 
-fn try_help(c: &mut Colorizer) -> io::Result<()> {
-    c.none("\n\nFor more information try ")?;
-    c.good("--help")?;
-    c.none("\n")
+fn try_help(c: &mut Colorizer) {
+    c.none("\n\nFor more information try ");
+    c.good("--help");
+    c.none("\n");
 }
 
 impl Error {
@@ -440,604 +436,427 @@ impl Error {
         safe_exit(0)
     }
 
-    #[allow(unused)] // requested by @pksunkara
-    pub(crate) fn group_conflict<O, U>(
-        group: &ArgGroup,
-        other: Option<O>,
-        usage: U,
-        color: ColorChoice,
-    ) -> io::Result<Self>
-    where
-        O: Into<String>,
-        U: Display,
-    {
-        let mut v = vec![group.name.to_owned()];
-        let mut c = Colorizer::new(true, color);
-
-        start_error(&mut c, "The argument '")?;
-        c.warning(group.name)?;
-        c.none("' cannot be used with ")?;
-
-        let cause = match other {
-            Some(name) => {
-                let n = name.into();
-                v.push(n.clone());
-
-                c.none("'")?;
-                c.warning(&*n)?;
-                c.none("'")?;
-
-                format!("The argument '{}' cannot be used with '{}'", group.name, n)
-            }
-            None => {
-                let n = "one or more of the other specified arguments";
-
-                c.none(n)?;
-
-                format!("The argument '{}' cannot be used with {}", group.name, n)
-            }
-        };
-
-        put_usage(&mut c, usage)?;
-        try_help(&mut c)?;
-
-        Ok(Error {
-            cause,
-            message: c,
-            kind: ErrorKind::ArgumentConflict,
-            info: Some(v),
-        })
-    }
-
-    pub(crate) fn argument_conflict<O, U>(
+    pub(crate) fn argument_conflict(
         arg: &Arg,
-        other: Option<O>,
-        usage: U,
+        other: Option<String>,
+        usage: String,
         color: ColorChoice,
-    ) -> io::Result<Self>
-    where
-        O: Into<String>,
-        U: Display,
-    {
-        let mut v = vec![arg.name.to_owned()];
+    ) -> Self {
         let mut c = Colorizer::new(true, color);
+        let arg = arg.to_string();
 
-        start_error(&mut c, "The argument '")?;
-        c.warning(&arg.to_string())?;
-        c.none("' cannot be used with ")?;
-
-        let cause = match other {
-            Some(name) => {
-                let n = name.into();
-                v.push(n.clone());
+        start_error(&mut c, "The argument '");
+        c.warning(arg.clone());
+        c.none("' cannot be used with ");
 
-                c.none("'")?;
-                c.warning(&*n)?;
-                c.none("'")?;
-
-                format!("The argument '{}' cannot be used with '{}'", arg, n)
+        match other {
+            Some(ref name) => {
+                c.none("'");
+                c.warning(name);
+                c.none("'");
             }
             None => {
-                let n = "one or more of the other specified arguments";
-
-                c.none(n)?;
-
-                format!("The argument '{}' cannot be used with {}", arg, n)
+                c.none("one or more of the other specified arguments");
             }
         };
 
-        put_usage(&mut c, usage)?;
-        try_help(&mut c)?;
+        put_usage(&mut c, usage);
+        try_help(&mut c);
 
-        Ok(Error {
-            cause,
+        let mut info = vec![arg];
+        if let Some(other) = other {
+            info.push(other);
+        }
+
+        Error {
             message: c,
             kind: ErrorKind::ArgumentConflict,
-            info: Some(v),
-        })
+            info,
+        }
     }
 
-    pub(crate) fn empty_value<U>(arg: &Arg, usage: U, color: ColorChoice) -> io::Result<Self>
-    where
-        U: Display,
-    {
+    pub(crate) fn empty_value(arg: &Arg, usage: String, color: ColorChoice) -> Self {
         let mut c = Colorizer::new(true, color);
+        let arg = arg.to_string();
+
+        start_error(&mut c, "The argument '");
+        c.warning(arg.clone());
+        c.none("' requires a value but none was supplied");
+        put_usage(&mut c, usage);
+        try_help(&mut c);
 
-        start_error(&mut c, "The argument '")?;
-        c.warning(&arg.to_string())?;
-        c.none("' requires a value but none was supplied")?;
-        put_usage(&mut c, usage)?;
-        try_help(&mut c)?;
-
-        Ok(Error {
-            cause: format!(
-                "The argument '{}' requires a value but none was supplied",
-                arg
-            ),
+        Error {
             message: c,
             kind: ErrorKind::EmptyValue,
-            info: Some(vec![arg.name.to_owned()]),
-        })
+            info: vec![arg],
+        }
     }
 
-    pub(crate) fn invalid_value<B, G, U>(
-        bad_val: B,
+    pub(crate) fn invalid_value<G>(
+        bad_val: String,
         good_vals: &[G],
         arg: &Arg,
-        usage: U,
+        usage: String,
         color: ColorChoice,
-    ) -> io::Result<Self>
+    ) -> Self
     where
-        B: AsRef<str>,
         G: AsRef<str> + Display,
-        U: Display,
     {
         let mut c = Colorizer::new(true, color);
-        let suffix = suggestions::did_you_mean(bad_val.as_ref(), good_vals.iter()).pop();
+        let suffix = suggestions::did_you_mean(&bad_val, good_vals.iter()).pop();
 
         let mut sorted: Vec<String> = good_vals.iter().map(|v| v.to_string()).collect();
         sorted.sort();
 
-        start_error(&mut c, "'")?;
-        c.warning(bad_val.as_ref())?;
-        c.none("' isn't a valid value for '")?;
-        c.warning(&arg.to_string())?;
-        c.none("'\n\t[possible values: ")?;
+        start_error(&mut c, "'");
+        c.warning(bad_val.clone());
+        c.none("' isn't a valid value for '");
+        c.warning(arg.to_string());
+        c.none("'\n\t[possible values: ");
 
         if let Some((last, elements)) = sorted.split_last() {
             for v in elements {
-                c.good(v)?;
-                c.none(", ")?;
+                c.good(v);
+                c.none(", ");
             }
 
-            c.good(last)?;
+            c.good(last);
         }
 
-        c.none("]")?;
+        c.none("]");
 
         if let Some(val) = suffix {
-            c.none("\n\n\tDid you mean '")?;
-            c.good(&val)?;
-            c.none("'?")?;
+            c.none("\n\n\tDid you mean '");
+            c.good(val);
+            c.none("'?");
         }
 
-        put_usage(&mut c, usage)?;
-        try_help(&mut c)?;
-
-        Ok(Error {
-            cause: format!(
-                "'{}' isn't a valid value for '{}'\n\t\
-                 [possible values: {}]",
-                bad_val.as_ref(),
-                arg,
-                sorted.join(", ")
-            ),
+        put_usage(&mut c, usage);
+        try_help(&mut c);
+
+        let mut info = vec![arg.to_string(), bad_val];
+        info.extend(sorted);
+
+        Error {
             message: c,
             kind: ErrorKind::InvalidValue,
-            info: Some(vec![arg.name.to_owned(), bad_val.as_ref().to_owned()]),
-        })
+            info: vec![],
+        }
     }
 
-    pub(crate) fn invalid_subcommand<S, D, N, U>(
-        subcmd: S,
-        did_you_mean: D,
-        name: N,
-        usage: U,
+    pub(crate) fn invalid_subcommand(
+        subcmd: String,
+        did_you_mean: String,
+        name: String,
+        usage: String,
         color: ColorChoice,
-    ) -> io::Result<Self>
-    where
-        S: Into<String>,
-        D: AsRef<str> + Display,
-        N: Display,
-        U: Display,
-    {
-        let s = subcmd.into();
+    ) -> Self {
         let mut c = Colorizer::new(true, color);
 
-        start_error(&mut c, "The subcommand '")?;
-        c.warning(&*s)?;
-        c.none("' wasn't recognized\n\n\tDid you mean ")?;
-        c.good(did_you_mean.as_ref())?;
-        c.none("")?;
-        c.none(&format!(
+        start_error(&mut c, "The subcommand '");
+        c.warning(subcmd.clone());
+        c.none("' wasn't recognized\n\n\tDid you mean ");
+        c.good(did_you_mean);
+        c.none("");
+        c.none(format!(
             "?\n\nIf you believe you received this message in error, try re-running with '{} ",
             name
-        ))?;
-        c.good("--")?;
-        c.none(&format!(" {}'", &*s))?;
-        put_usage(&mut c, usage)?;
-        try_help(&mut c)?;
-
-        Ok(Error {
-            cause: format!("The subcommand '{}' wasn't recognized", s),
+        ));
+        c.good("--");
+        c.none(format!(" {}'", subcmd));
+        put_usage(&mut c, usage);
+        try_help(&mut c);
+
+        Error {
             message: c,
             kind: ErrorKind::InvalidSubcommand,
-            info: Some(vec![s]),
-        })
+            info: vec![subcmd],
+        }
     }
 
-    pub(crate) fn unrecognized_subcommand<S, N>(
-        subcmd: S,
-        name: N,
+    pub(crate) fn unrecognized_subcommand(
+        subcmd: String,
+        name: String,
         color: ColorChoice,
-    ) -> io::Result<Self>
-    where
-        S: Into<String>,
-        N: Display,
-    {
-        let s = subcmd.into();
+    ) -> Self {
         let mut c = Colorizer::new(true, color);
 
-        start_error(&mut c, " The subcommand '")?;
-        c.warning(&*s)?;
-        c.none("' wasn't recognized\n\n")?;
-        c.warning("USAGE:")?;
-        c.none(&format!("\n\t{} help <subcommands>...", name))?;
-        try_help(&mut c)?;
+        start_error(&mut c, " The subcommand '");
+        c.warning(subcmd.clone());
+        c.none("' wasn't recognized\n\n");
+        c.warning("USAGE:");
+        c.none(format!("\n\t{} help <subcommands>...", name));
+        try_help(&mut c);
 
-        Ok(Error {
-            cause: format!("The subcommand '{}' wasn't recognized", s),
+        Error {
             message: c,
             kind: ErrorKind::UnrecognizedSubcommand,
-            info: Some(vec![s]),
-        })
+            info: vec![subcmd],
+        }
     }
 
-    pub(crate) fn missing_required_argument<R, U>(
-        required: VecDeque<R>,
-        usage: U,
+    pub(crate) fn missing_required_argument(
+        required: Vec<String>,
+        usage: String,
         color: ColorChoice,
-    ) -> io::Result<Self>
-    where
-        R: Display,
-        U: Display,
-    {
+    ) -> Self {
         let mut c = Colorizer::new(true, color);
 
-        let cause = format!(
-            "The following required arguments were not provided:{}",
-            required
-                .iter()
-                .map(|x| format!("\n    {}", x))
-                .collect::<Vec<_>>()
-                .join(""),
-        );
-
         start_error(
             &mut c,
             "The following required arguments were not provided:",
-        )?;
+        );
 
         let mut info = vec![];
         for v in required {
-            c.none("\n    ")?;
-            c.good(&v.to_string())?;
+            c.none("\n    ");
+            c.good(v.to_string());
             info.push(v.to_string());
         }
 
-        put_usage(&mut c, usage)?;
-        try_help(&mut c)?;
+        put_usage(&mut c, usage);
+        try_help(&mut c);
 
-        Ok(Error {
-            cause,
+        Error {
             message: c,
             kind: ErrorKind::MissingRequiredArgument,
-            info: Some(info),
-        })
+            info,
+        }
     }
 
-    pub(crate) fn missing_subcommand<N, U>(
-        name: N,
-        usage: U,
-        color: ColorChoice,
-    ) -> io::Result<Self>
-    where
-        N: AsRef<str> + Display,
-        U: Display,
-    {
+    pub(crate) fn missing_subcommand(name: String, usage: String, color: ColorChoice) -> Self {
         let mut c = Colorizer::new(true, color);
 
-        start_error(&mut c, "'")?;
-        c.warning(name.as_ref())?;
-        c.none("' requires a subcommand, but one was not provided")?;
-        put_usage(&mut c, usage)?;
-        try_help(&mut c)?;
+        start_error(&mut c, "'");
+        c.warning(name);
+        c.none("' requires a subcommand, but one was not provided");
+        put_usage(&mut c, usage);
+        try_help(&mut c);
 
-        Ok(Error {
-            cause: format!("'{}' requires a subcommand, but one was not provided", name),
+        Error {
             message: c,
             kind: ErrorKind::MissingSubcommand,
-            info: None,
-        })
+            info: vec![],
+        }
     }
 
-    pub(crate) fn invalid_utf8<U>(usage: U, color: ColorChoice) -> io::Result<Self>
-    where
-        U: Display,
-    {
+    pub(crate) fn invalid_utf8(usage: String, color: ColorChoice) -> Self {
         let mut c = Colorizer::new(true, color);
 
-        let cause = "Invalid UTF-8 was detected in one or more arguments";
-
-        start_error(&mut c, cause)?;
-        put_usage(&mut c, usage)?;
-        try_help(&mut c)?;
+        start_error(
+            &mut c,
+            "Invalid UTF-8 was detected in one or more arguments",
+        );
+        put_usage(&mut c, usage);
+        try_help(&mut c);
 
-        Ok(Error {
-            cause: cause.to_string(),
+        Error {
             message: c,
             kind: ErrorKind::InvalidUtf8,
-            info: None,
-        })
+            info: vec![],
+        }
     }
 
-    pub(crate) fn too_many_values<V, U>(
-        val: V,
+    pub(crate) fn too_many_values(
+        val: String,
         arg: &Arg,
-        usage: U,
+        usage: String,
         color: ColorChoice,
-    ) -> io::Result<Self>
-    where
-        V: AsRef<str> + Display + ToOwned,
-        U: Display,
-    {
-        let v = val.as_ref();
+    ) -> Self {
         let mut c = Colorizer::new(true, color);
 
-        start_error(&mut c, "The value '")?;
-        c.warning(v)?;
-        c.none("' was provided to '")?;
-        c.warning(&arg.to_string())?;
-        c.none("' but it wasn't expecting any more values")?;
-        put_usage(&mut c, usage)?;
-        try_help(&mut c)?;
-
-        Ok(Error {
-            cause: format!(
-                "The value '{}' was provided to '{}', but it wasn't expecting any more values",
-                v, arg
-            ),
+        start_error(&mut c, "The value '");
+        c.warning(val.clone());
+        c.none("' was provided to '");
+        c.warning(arg.to_string());
+        c.none("' but it wasn't expecting any more values");
+        put_usage(&mut c, usage);
+        try_help(&mut c);
+
+        Error {
             message: c,
             kind: ErrorKind::TooManyValues,
-            info: Some(vec![arg.name.to_owned(), v.to_owned()]),
-        })
+            info: vec![arg.to_string(), val],
+        }
     }
 
-    pub(crate) fn too_few_values<U>(
+    pub(crate) fn too_few_values(
         arg: &Arg,
         min_vals: u64,
         curr_vals: usize,
-        usage: U,
+        usage: String,
         color: ColorChoice,
-    ) -> io::Result<Self>
-    where
-        U: Display,
-    {
+    ) -> Self {
         let mut c = Colorizer::new(true, color);
         let verb = Error::singular_or_plural(curr_vals);
 
-        start_error(&mut c, "The argument '")?;
-        c.warning(&arg.to_string())?;
-        c.none("' requires at least ")?;
-        c.warning(&min_vals.to_string())?;
-        c.none(" values, but only ")?;
-        c.warning(&curr_vals.to_string())?;
-        c.none(&format!(" {} provided", verb))?;
-        put_usage(&mut c, usage)?;
-        try_help(&mut c)?;
-
-        Ok(Error {
-            cause: format!(
-                "The argument '{}' requires at least {} values, but only {} {} provided",
-                arg, min_vals, curr_vals, verb
-            ),
+        start_error(&mut c, "The argument '");
+        c.warning(arg.to_string());
+        c.none("' requires at least ");
+        c.warning(min_vals.to_string());
+        c.none(" values, but only ");
+        c.warning(curr_vals.to_string());
+        c.none(format!(" {} provided", verb));
+        put_usage(&mut c, usage);
+        try_help(&mut c);
+
+        Error {
             message: c,
             kind: ErrorKind::TooFewValues,
-            info: Some(vec![arg.name.to_owned()]),
-        })
+            info: vec![arg.to_string(), curr_vals.to_string(), min_vals.to_string()],
+        }
     }
 
     pub(crate) fn value_validation(
-        arg: Option<&Arg>,
-        err: &str,
+        arg: String,
+        val: String,
+        err: String,
         color: ColorChoice,
-    ) -> io::Result<Self> {
+    ) -> Self {
         let mut c = Colorizer::new(true, color);
 
-        start_error(&mut c, "Invalid value")?;
+        start_error(&mut c, "Invalid value");
 
-        if let Some(a) = arg {
-            c.none(" for '")?;
-            c.warning(&a.to_string())?;
-            c.none("'")?;
-        }
+        c.none(" for '");
+        c.warning(arg.clone());
+        c.none("'");
 
-        c.none(&format!(": {}", err))?;
-        try_help(&mut c)?;
-
-        Ok(Error {
-            cause: format!(
-                "Invalid value{}: {}",
-                if let Some(a) = arg {
-                    format!(" for '{}'", a)
-                } else {
-                    String::new()
-                },
-                err
-            ),
+        c.none(format!(": {}", err));
+        try_help(&mut c);
+
+        Error {
             message: c,
             kind: ErrorKind::ValueValidation,
-            info: None,
-        })
-    }
-
-    pub(crate) fn value_validation_auto(err: &str) -> io::Result<Self> {
-        let n: Option<&Arg> = None;
-        Error::value_validation(n, err, ColorChoice::Auto)
+            info: vec![arg, val, err],
+        }
     }
 
-    pub(crate) fn wrong_number_of_values<U>(
+    pub(crate) fn wrong_number_of_values(
         arg: &Arg,
         num_vals: u64,
         curr_vals: usize,
-        usage: U,
+        usage: String,
         color: ColorChoice,
-    ) -> io::Result<Self>
-    where
-        U: Display,
-    {
+    ) -> Self {
         let mut c = Colorizer::new(true, color);
         let verb = Error::singular_or_plural(curr_vals);
 
-        start_error(&mut c, "The argument '")?;
-        c.warning(&arg.to_string())?;
-        c.none("' requires ")?;
-        c.warning(&num_vals.to_string())?;
-        c.none(" values, but ")?;
-        c.warning(&curr_vals.to_string())?;
-        c.none(&format!(" {} provided", verb))?;
-        put_usage(&mut c, usage)?;
-        try_help(&mut c)?;
-
-        Ok(Error {
-            cause: format!(
-                "The argument '{}' requires {} values, but {} {} provided",
-                arg, num_vals, curr_vals, verb
-            ),
+        start_error(&mut c, "The argument '");
+        c.warning(arg.to_string());
+        c.none("' requires ");
+        c.warning(num_vals.to_string());
+        c.none(" values, but ");
+        c.warning(curr_vals.to_string());
+        c.none(format!(" {} provided", verb));
+        put_usage(&mut c, usage);
+        try_help(&mut c);
+
+        Error {
             message: c,
             kind: ErrorKind::WrongNumberOfValues,
-            info: Some(vec![arg.name.to_owned()]),
-        })
+            info: vec![arg.to_string(), curr_vals.to_string(), num_vals.to_string()],
+        }
     }
 
-    pub(crate) fn unexpected_multiple_usage<U>(
-        arg: &Arg,
-        usage: U,
-        color: ColorChoice,
-    ) -> io::Result<Self>
-    where
-        U: Display,
-    {
+    pub(crate) fn unexpected_multiple_usage(arg: &Arg, usage: String, color: ColorChoice) -> Self {
         let mut c = Colorizer::new(true, color);
+        let arg = arg.to_string();
 
-        start_error(&mut c, "The argument '")?;
-        c.warning(&arg.to_string())?;
-        c.none("' was provided more than once, but cannot be used multiple times")?;
-        put_usage(&mut c, usage)?;
-        try_help(&mut c)?;
-
-        Ok(Error {
-            cause: format!(
-                "The argument '{}' was provided more than once, but cannot be used multiple times",
-                arg
-            ),
+        start_error(&mut c, "The argument '");
+        c.warning(arg.clone());
+        c.none("' was provided more than once, but cannot be used multiple times");
+        put_usage(&mut c, usage);
+        try_help(&mut c);
+
+        Error {
             message: c,
             kind: ErrorKind::UnexpectedMultipleUsage,
-            info: Some(vec![arg.name.to_owned()]),
-        })
+            info: vec![arg],
+        }
     }
 
-    pub(crate) fn unknown_argument<A, U>(
-        arg: A,
+    pub(crate) fn unknown_argument(
+        arg: String,
         did_you_mean: Option<(String, Option<String>)>,
-        usage: U,
+        usage: String,
         color: ColorChoice,
-    ) -> io::Result<Self>
-    where
-        A: Into<String>,
-        U: Display,
-    {
-        let a = arg.into();
+    ) -> Self {
         let mut c = Colorizer::new(true, color);
 
-        start_error(&mut c, "Found argument '")?;
-        c.warning(&*a)?;
-        c.none("' which wasn't expected, or isn't valid in this context")?;
+        start_error(&mut c, "Found argument '");
+        c.warning(arg.clone());
+        c.none("' which wasn't expected, or isn't valid in this context");
 
         if let Some(s) = did_you_mean {
-            c.none("\n\n\tDid you mean ")?;
+            c.none("\n\n\tDid you mean ");
 
             if let Some(subcmd) = s.1 {
-                c.none("to put '")?;
-                c.good(&format!("--{}", &s.0))?;
-                c.none("' after the subcommand '")?;
-                c.good(&subcmd)?;
-                c.none("'?")?;
+                c.none("to put '");
+                c.good(format!("--{}", &s.0));
+                c.none("' after the subcommand '");
+                c.good(subcmd);
+                c.none("'?");
             } else {
-                c.none("'")?;
-                c.good(&format!("--{}", &s.0))?;
-                c.none("'?")?;
+                c.none("'");
+                c.good(format!("--{}", &s.0));
+                c.none("'?");
             }
         }
 
-        c.none(&format!(
+        c.none(format!(
             "\n\nIf you tried to supply `{}` as a PATTERN use `-- {}`",
-            a, a
-        ))?;
-        put_usage(&mut c, usage)?;
-        try_help(&mut c)?;
-
-        Ok(Error {
-            cause: format!(
-                "Found argument '{}' which wasn't expected, or isn't valid in this context",
-                a
-            ),
+            arg, arg
+        ));
+        put_usage(&mut c, usage);
+        try_help(&mut c);
+
+        Error {
             message: c,
             kind: ErrorKind::UnknownArgument,
-            info: Some(vec![a]),
-        })
+            info: vec![arg],
+        }
     }
 
-    pub(crate) fn argument_not_found_auto<A>(arg: A) -> io::Result<Self>
-    where
-        A: Into<String>,
-    {
-        let a = arg.into();
+    pub(crate) fn argument_not_found_auto(arg: String) -> Self {
         let mut c = Colorizer::new(true, ColorChoice::Auto);
 
-        start_error(&mut c, "The argument '")?;
-        c.warning(&*a)?;
-        c.none("' wasn't found")?;
-        try_help(&mut c)?;
+        start_error(&mut c, "The argument '");
+        c.warning(arg.clone());
+        c.none("' wasn't found");
+        try_help(&mut c);
 
-        Ok(Error {
-            cause: format!("The argument '{}' wasn't found", a),
+        Error {
             message: c,
             kind: ErrorKind::ArgumentNotFound,
-            info: Some(vec![a]),
-        })
+            info: vec![arg],
+        }
     }
 
     /// Create an error with a custom description.
     ///
     /// This can be used in combination with `Error::exit` to exit your program
     /// with a custom error message.
-    pub fn with_description(description: impl Into<String>, kind: ErrorKind) -> io::Result<Self> {
+    pub fn with_description(description: String, kind: ErrorKind) -> Self {
         let mut c = Colorizer::new(true, ColorChoice::Auto);
 
-        let cause = description.into();
+        start_error(&mut c, description);
 
-        start_error(&mut c, &*cause)?;
-
-        Ok(Error {
-            cause,
+        Error {
             message: c,
             kind,
-            info: None,
-        })
+            info: vec![],
+        }
     }
 }
 
 impl From<io::Error> for Error {
     fn from(e: io::Error) -> Self {
         Error::with_description(e.to_string(), ErrorKind::Io)
-            .expect("Unable to build error message")
     }
 }
 
 impl From<fmt::Error> for Error {
     fn from(e: fmt::Error) -> Self {
         Error::with_description(e.to_string(), ErrorKind::Format)
-            .expect("Unable to build error message")
     }
 }
diff --git a/src/parse/matches/arg_matches.rs b/src/parse/matches/arg_matches.rs
index 25a7a42f..3fc3362b 100644
--- a/src/parse/matches/arg_matches.rs
+++ b/src/parse/matches/arg_matches.rs
@@ -14,7 +14,7 @@ use indexmap::IndexMap;
 // Internal
 use crate::{
     parse::MatchedArg,
-    util::{Id, Key},
+    util::{termcolor::ColorChoice, Id, Key},
     {Error, INVALID_UTF8},
 };
 
@@ -345,14 +345,16 @@ impl ArgMatches {
         <R as FromStr>::Err: Display,
     {
         if let Some(v) = self.value_of(name) {
-            v.parse::<R>().or_else(|e| {
-                Err(Error::value_validation_auto(&format!(
-                    "The argument '{}' isn't a valid value: {}",
-                    v, e
-                ))?)
+            v.parse::<R>().map_err(|e| {
+                let message = format!(
+                    "The argument '{}' isn't a valid value for '{}': {}",
+                    v, name, e
+                );
+
+                Error::value_validation(name.to_string(), v.to_string(), message, ColorChoice::Auto)
             })
         } else {
-            Err(Error::argument_not_found_auto(name)?)
+            Err(Error::argument_not_found_auto(name.to_string()))
         }
     }
 
@@ -431,16 +433,20 @@ impl ArgMatches {
     {
         if let Some(vals) = self.values_of(name) {
             vals.map(|v| {
-                v.parse::<R>().or_else(|e| {
-                    Err(Error::value_validation_auto(&format!(
-                        "The argument '{}' isn't a valid value: {}",
-                        v, e
-                    ))?)
+                v.parse::<R>().map_err(|e| {
+                    let message = format!("The argument '{}' isn't a valid value: {}", v, e);
+
+                    Error::value_validation(
+                        name.to_string(),
+                        v.to_string(),
+                        message,
+                        ColorChoice::Auto,
+                    )
                 })
             })
             .collect()
         } else {
-            Err(Error::argument_not_found_auto(name)?)
+            Err(Error::argument_not_found_auto(name.to_string()))
         }
     }
 
diff --git a/src/parse/parser.rs b/src/parse/parser.rs
index 16cc7056..ef7acce3 100644
--- a/src/parse/parser.rs
+++ b/src/parse/parser.rs
@@ -2,7 +2,6 @@
 use std::{
     cell::Cell,
     ffi::{OsStr, OsString},
-    io::Write,
 };
 
 // Internal
@@ -483,11 +482,11 @@ impl<'help, 'app> Parser<'help, 'app> {
                                     || lossy_arg.parse::<f64>().is_ok())
                                 {
                                     return Err(ClapError::unknown_argument(
-                                        lossy_arg,
+                                        lossy_arg.to_string(),
                                         None,
-                                        &*Usage::new(self).create_usage_with_title(&[]),
+                                        Usage::new(self).create_usage_with_title(&[]),
                                         self.app.color(),
-                                    )?);
+                                    ));
                                 }
                             }
                             ParseResult::Opt(ref id)
@@ -537,12 +536,16 @@ impl<'help, 'app> Parser<'help, 'app> {
                         let cands: Vec<_> =
                             cands.iter().map(|cand| format!("'{}'", cand)).collect();
                         return Err(ClapError::invalid_subcommand(
-                            arg_os.to_string_lossy().into_owned(),
+                            arg_os.to_string_lossy().to_string(),
                             cands.join(" or "),
-                            self.app.bin_name.as_ref().unwrap_or(&self.app.name),
-                            &*Usage::new(self).create_usage_with_title(&[]),
+                            self.app
+                                .bin_name
+                                .as_ref()
+                                .unwrap_or(&self.app.name)
+                                .to_string(),
+                            Usage::new(self).create_usage_with_title(&[]),
                             self.app.color(),
-                        )?);
+                        ));
                     }
                 }
             }
@@ -630,11 +633,11 @@ impl<'help, 'app> Parser<'help, 'app> {
             {
                 if p.is_set(ArgSettings::Last) && !self.is_set(AS::TrailingValues) {
                     return Err(ClapError::unknown_argument(
-                        &*arg_os.to_string_lossy(),
+                        arg_os.to_string_lossy().to_string(),
                         None,
-                        &*Usage::new(self).create_usage_with_title(&[]),
+                        Usage::new(self).create_usage_with_title(&[]),
                         self.app.color(),
-                    )?);
+                    ));
                 }
 
                 if !self.is_set(AS::TrailingValues)
@@ -672,9 +675,9 @@ impl<'help, 'app> Parser<'help, 'app> {
                     None => {
                         if !self.is_set(AS::StrictUtf8) {
                             return Err(ClapError::invalid_utf8(
-                                &*Usage::new(self).create_usage_with_title(&[]),
+                                Usage::new(self).create_usage_with_title(&[]),
                                 self.app.color(),
-                            )?);
+                            ));
                         }
                         arg_os.to_string_lossy().into_owned()
                     }
@@ -686,9 +689,9 @@ impl<'help, 'app> Parser<'help, 'app> {
                 while let Some((v, _)) = it.next(None) {
                     if v.to_str().is_none() && !self.is_set(AS::StrictUtf8) {
                         return Err(ClapError::invalid_utf8(
-                            &*Usage::new(self).create_usage_with_title(&[]),
+                            Usage::new(self).create_usage_with_title(&[]),
                             self.app.color(),
-                        )?);
+                        ));
                     }
                     sc_m.add_val_to(&Id::empty_hash(), v.to_os_string(), ValueType::CommandLine);
                 }
@@ -708,11 +711,11 @@ impl<'help, 'app> Parser<'help, 'app> {
                 && !self.is_set(AS::InferSubcommands)
             {
                 return Err(ClapError::unknown_argument(
-                    &*arg_os.to_string_lossy(),
+                    arg_os.to_string_lossy().to_string(),
                     None,
-                    &*Usage::new(self).create_usage_with_title(&[]),
+                    Usage::new(self).create_usage_with_title(&[]),
                     self.app.color(),
-                )?);
+                ));
             } else if !has_args || self.is_set(AS::InferSubcommands) && self.has_subcommands() {
                 let cands = suggestions::did_you_mean(
                     &*arg_os.to_string_lossy(),
@@ -721,26 +724,34 @@ impl<'help, 'app> Parser<'help, 'app> {
                 if !cands.is_empty() {
                     let cands: Vec<_> = cands.iter().map(|cand| format!("'{}'", cand)).collect();
                     return Err(ClapError::invalid_subcommand(
-                        arg_os.to_string_lossy().into_owned(),
+                        arg_os.to_string_lossy().to_string(),
                         cands.join(" or "),
-                        self.app.bin_name.as_ref().unwrap_or(&self.app.name),
-                        &*Usage::new(self).create_usage_with_title(&[]),
+                        self.app
+                            .bin_name
+                            .as_ref()
+                            .unwrap_or(&self.app.name)
+                            .to_string(),
+                        Usage::new(self).create_usage_with_title(&[]),
                         self.app.color(),
-                    )?);
+                    ));
                 } else {
                     return Err(ClapError::unrecognized_subcommand(
-                        arg_os.to_string_lossy().into_owned(),
-                        self.app.bin_name.as_ref().unwrap_or(&self.app.name),
+                        arg_os.to_string_lossy().to_string(),
+                        self.app
+                            .bin_name
+                            .as_ref()
+                            .unwrap_or(&self.app.name)
+                            .to_string(),
                         self.app.color(),
-                    )?);
+                    ));
                 }
             } else {
                 return Err(ClapError::unknown_argument(
-                    &*arg_os.to_string_lossy(),
+                    arg_os.to_string_lossy().to_string(),
                     None,
-                    &*Usage::new(self).create_usage_with_title(&[]),
+                    Usage::new(self).create_usage_with_title(&[]),
                     self.app.color(),
-                )?);
+                ));
             }
         }
 
@@ -756,18 +767,17 @@ impl<'help, 'app> Parser<'help, 'app> {
             } else if self.is_set(AS::SubcommandRequired) {
                 let bn = self.app.bin_name.as_ref().unwrap_or(&self.app.name);
                 return Err(ClapError::missing_subcommand(
-                    bn,
-                    &Usage::new(self).create_usage_with_title(&[]),
+                    bn.to_string(),
+                    Usage::new(self).create_usage_with_title(&[]),
                     self.app.color(),
-                )?);
+                ));
             } else if self.is_set(AS::SubcommandRequiredElseHelp) {
                 debug!("Parser::get_matches_with: SubcommandRequiredElseHelp=true");
                 let message = self.write_help_err()?;
                 return Err(ClapError {
-                    cause: String::new(),
                     message,
                     kind: ErrorKind::MissingArgumentOrSubcommand,
-                    info: None,
+                    info: vec![],
                 });
             }
         }
@@ -941,10 +951,14 @@ impl<'help, 'app> Parser<'help, 'app> {
                     }
                 } else {
                     return Err(ClapError::unrecognized_subcommand(
-                        cmd.to_string_lossy().into_owned(),
-                        self.app.bin_name.as_ref().unwrap_or(&self.app.name),
+                        cmd.to_string_lossy().to_string(),
+                        self.app
+                            .bin_name
+                            .as_ref()
+                            .unwrap_or(&self.app.name)
+                            .to_string(),
                         self.app.color(),
-                    )?);
+                    ));
                 }
 
                 bin_name = format!("{} {}", bin_name, &sc.name);
@@ -1327,11 +1341,11 @@ impl<'help, 'app> Parser<'help, 'app> {
                 let arg = format!("-{}", c);
 
                 return Err(ClapError::unknown_argument(
-                    &*arg,
+                    arg,
                     None,
-                    &*Usage::new(self).create_usage_with_title(&[]),
+                    Usage::new(self).create_usage_with_title(&[]),
                     self.app.color(),
-                )?);
+                ));
             }
         }
         Ok(ret)
@@ -1360,9 +1374,9 @@ impl<'help, 'app> Parser<'help, 'app> {
                 debug!("Found Empty - Error");
                 return Err(ClapError::empty_value(
                     opt,
-                    &*Usage::new(self).create_usage_with_title(&[]),
+                    Usage::new(self).create_usage_with_title(&[]),
                     self.app.color(),
-                )?);
+                ));
             }
             debug!("Found - {:?}, len: {}", v, v.len());
             debug!(
@@ -1375,9 +1389,9 @@ impl<'help, 'app> Parser<'help, 'app> {
             debug!("None, but requires equals...Error");
             return Err(ClapError::empty_value(
                 opt,
-                &*Usage::new(self).create_usage_with_title(&[]),
+                Usage::new(self).create_usage_with_title(&[]),
                 self.app.color(),
-            )?);
+            ));
         } else if needs_eq && min_vals_zero {
             debug!("None and requires equals, but min_vals == 0");
             if !opt.default_missing_vals.is_empty() {
@@ -1722,24 +1736,16 @@ impl<'help, 'app> Parser<'help, 'app> {
             .collect();
 
         Err(ClapError::unknown_argument(
-            &*format!("--{}", arg),
+            format!("--{}", arg),
             did_you_mean,
-            &*Usage::new(self).create_usage_with_title(&*used),
+            Usage::new(self).create_usage_with_title(&*used),
             self.app.color(),
-        )?)
-    }
-
-    // Prints the version to the user and exits if quit=true
-    fn print_version<W: Write>(&self, w: &mut W, use_long: bool) -> ClapResult<()> {
-        self.app._write_version(w, use_long)?;
-        w.flush().map_err(ClapError::from)
+        ))
     }
 
     pub(crate) fn write_help_err(&self) -> ClapResult<Colorizer> {
         let mut c = Colorizer::new(true, self.color_help());
-
         Help::new(HelpWriter::Buffer(&mut c), self, false).write_help()?;
-
         Ok(c)
     }
 
@@ -1753,12 +1759,11 @@ impl<'help, 'app> Parser<'help, 'app> {
         let mut c = Colorizer::new(false, self.color_help());
 
         match Help::new(HelpWriter::Buffer(&mut c), self, use_long).write_help() {
-            Err(e) => e,
+            Err(e) => e.into(),
             _ => ClapError {
-                cause: String::new(),
                 message: c,
                 kind: ErrorKind::DisplayHelp,
-                info: None,
+                info: vec![],
             },
         }
     }
@@ -1766,16 +1771,13 @@ impl<'help, 'app> Parser<'help, 'app> {
     fn version_err(&self, use_long: bool) -> ClapError {
         debug!("Parser::version_err");
 
-        let mut c = Colorizer::new(false, self.app.color());
-
-        match self.print_version(&mut c, use_long) {
-            Err(e) => e,
-            _ => ClapError {
-                cause: String::new(),
-                message: c,
-                kind: ErrorKind::DisplayVersion,
-                info: None,
-            },
+        let msg = self.app._render_version(use_long);
+        let mut c = Colorizer::new(false, self.color_help());
+        c.none(msg);
+        ClapError {
+            message: c,
+            kind: ErrorKind::DisplayVersion,
+            info: vec![],
         }
     }
 }
diff --git a/src/parse/validator.rs b/src/parse/validator.rs
index 254c4078..3cde1894 100644
--- a/src/parse/validator.rs
+++ b/src/parse/validator.rs
@@ -47,9 +47,9 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
             if should_err {
                 return Err(Error::empty_value(
                     o,
-                    &*Usage::new(self.p).create_usage_with_title(&[]),
+                    Usage::new(self.p).create_usage_with_title(&[]),
                     self.p.app.color(),
-                )?);
+                ));
             }
         }
 
@@ -59,10 +59,9 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
         {
             let message = self.p.write_help_err()?;
             return Err(Error {
-                cause: String::new(),
                 message,
                 kind: ErrorKind::MissingArgumentOrSubcommand,
-                info: None,
+                info: vec![],
             });
         }
         self.validate_conflicts(matcher)?;
@@ -89,9 +88,9 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
                     val
                 );
                 return Err(Error::invalid_utf8(
-                    &*Usage::new(self.p).create_usage_with_title(&[]),
+                    Usage::new(self.p).create_usage_with_title(&[]),
                     self.p.app.color(),
-                )?);
+                ));
             }
             if !arg.possible_vals.is_empty() {
                 debug!(
@@ -117,12 +116,12 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
                         .cloned()
                         .collect();
                     return Err(Error::invalid_value(
-                        val_str,
+                        val_str.to_string(),
                         &arg.possible_vals,
                         arg,
-                        &*Usage::new(self.p).create_usage_with_title(&*used),
+                        Usage::new(self.p).create_usage_with_title(&used),
                         self.p.app.color(),
-                    )?);
+                    ));
                 }
             }
             if !arg.is_set(ArgSettings::AllowEmptyValues)
@@ -132,9 +131,9 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
                 debug!("Validator::validate_arg_values: illegal empty val found");
                 return Err(Error::empty_value(
                     arg,
-                    &*Usage::new(self.p).create_usage_with_title(&[]),
+                    Usage::new(self.p).create_usage_with_title(&[]),
                     self.p.app.color(),
-                )?);
+                ));
             }
 
             // FIXME: `(&mut *vtor)(args...)` can be simplified to `vtor(args...)`
@@ -145,7 +144,12 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
                 let mut vtor = vtor.lock().unwrap();
                 if let Err(e) = (&mut *vtor)(&*val.to_string_lossy()) {
                     debug!("error");
-                    return Err(Error::value_validation(Some(arg), &e, self.p.app.color())?);
+                    return Err(Error::value_validation(
+                        arg.to_string(),
+                        val.to_string_lossy().to_string(),
+                        e,
+                        self.p.app.color(),
+                    ));
                 } else {
                     debug!("good");
                 }
@@ -156,10 +160,11 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
                 if let Err(e) = (&mut *vtor)(val) {
                     debug!("error");
                     return Err(Error::value_validation(
-                        Some(arg),
-                        &(*e).to_string(),
+                        arg.to_string(),
+                        val.to_string_lossy().into(),
+                        e,
                         self.p.app.color(),
-                    )?);
+                    ));
                 } else {
                     debug!("good");
                 }
@@ -213,9 +218,9 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
                         return Err(Error::argument_conflict(
                             latter_arg,
                             Some(former_arg.to_string()),
-                            &*usg,
+                            usg,
                             self.p.app.color(),
-                        )?);
+                        ));
                     }
                 }
             }
@@ -234,9 +239,9 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
             return Err(Error::argument_conflict(
                 &self.p.app[first],
                 c_with,
-                &*usg,
+                usg,
                 self.p.app.color(),
-            )?);
+            ));
         }
 
         panic!(INTERNAL_ERROR_MSG);
@@ -301,7 +306,7 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
                         c_with,
                         Usage::new(self.p).create_usage_with_title(&[]),
                         self.p.app.color(),
-                    )?);
+                    ));
                 }
             }
         }
@@ -428,9 +433,9 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
             // Not the first time, and we don't allow multiples
             return Err(Error::unexpected_multiple_usage(
                 a,
-                &*Usage::new(self.p).create_usage_with_title(&[]),
+                Usage::new(self.p).create_usage_with_title(&[]),
                 self.p.app.color(),
-            )?);
+            ));
         }
         Ok(())
     }
@@ -454,9 +459,9 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
                     } else {
                         ma.vals.len()
                     },
-                    &*Usage::new(self.p).create_usage_with_title(&[]),
+                    Usage::new(self.p).create_usage_with_title(&[]),
                     self.p.app.color(),
-                )?);
+                ));
             }
         }
         if let Some(num) = a.max_vals {
@@ -469,11 +474,12 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
                         .last()
                         .expect(INTERNAL_ERROR_MSG)
                         .to_str()
-                        .expect(INVALID_UTF8),
+                        .expect(INVALID_UTF8)
+                        .to_string(),
                     a,
-                    &*Usage::new(self.p).create_usage_with_title(&[]),
+                    Usage::new(self.p).create_usage_with_title(&[]),
                     self.p.app.color(),
-                )?);
+                ));
             }
         }
         let min_vals_zero = if let Some(num) = a.min_vals {
@@ -484,9 +490,9 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
                     a,
                     num,
                     ma.vals.len(),
-                    &*Usage::new(self.p).create_usage_with_title(&[]),
+                    Usage::new(self.p).create_usage_with_title(&[]),
                     self.p.app.color(),
-                )?);
+                ));
             }
             num == 0
         } else {
@@ -497,9 +503,9 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
         if a.is_set(ArgSettings::TakesValue) && !min_vals_zero && ma.vals.is_empty() {
             return Err(Error::empty_value(
                 a,
-                &*Usage::new(self.p).create_usage_with_title(&[]),
+                Usage::new(self.p).create_usage_with_title(&[]),
                 self.p.app.color(),
-            )?);
+            ));
         }
         Ok(())
     }
@@ -650,8 +656,8 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
 
         Err(Error::missing_required_argument(
             req_args,
-            &*usg.create_usage_with_title(&*used),
+            usg.create_usage_with_title(&*used),
             self.p.app.color(),
-        )?)
+        ))
     }
 }
diff --git a/src/util/termcolor.rs b/src/util/termcolor.rs
index 680e7fc8..97ac63f8 100644
--- a/src/util/termcolor.rs
+++ b/src/util/termcolor.rs
@@ -1,5 +1,3 @@
-use std::io::{stderr, stdout, Result, Write};
-
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub(crate) enum ColorChoice {
     Auto,
@@ -7,32 +5,9 @@ pub(crate) enum ColorChoice {
     Never,
 }
 
-pub(crate) type Buffer = Vec<u8>;
-
-pub(crate) struct BufferWriter {
-    use_stderr: bool,
-}
-
-impl BufferWriter {
-    pub(crate) fn buffer(&self) -> Buffer {
-        vec![]
-    }
-
-    pub(crate) fn stderr(_: ColorChoice) -> Self {
-        Self { use_stderr: true }
-    }
-
-    pub(crate) fn stdout(_: ColorChoice) -> Self {
-        Self { use_stderr: false }
-    }
-
-    pub(crate) fn print(&self, buf: &Buffer) -> Result<()> {
-        if self.use_stderr {
-            stderr().lock().write_all(buf)?;
-        } else {
-            stdout().lock().write_all(buf)?;
-        }
-
-        Ok(())
-    }
+#[derive(Debug)]
+pub(crate) enum Color {
+    Green,
+    Yellow,
+    Red,
 }


Test patch:
diff --git a/tests/conflicts.rs b/tests/conflicts.rs
index ef3a4b44..a81a2495 100644
--- a/tests/conflicts.rs
+++ b/tests/conflicts.rs
@@ -188,9 +188,11 @@ fn two_conflicting_arguments() {
 
     assert!(a.is_err());
     let a = a.unwrap_err();
-    assert_eq!(
-        a.cause,
-        "The argument \'--production\' cannot be used with \'--develop\'"
+    assert!(
+        a.to_string()
+            .contains("The argument \'--production\' cannot be used with \'--develop\'"),
+        "{}",
+        a
     );
 }
 
@@ -216,9 +218,11 @@ fn three_conflicting_arguments() {
 
     assert!(a.is_err());
     let a = a.unwrap_err();
-    assert_eq!(
-        a.cause,
-        "The argument \'--two\' cannot be used with \'--one\'"
+    assert!(
+        a.to_string()
+            .contains("The argument \'--two\' cannot be used with \'--one\'"),
+        "{}",
+        a
     );
 }
 
diff --git a/tests/validators.rs b/tests/validators.rs
index 2b8bb64a..7c9e71d7 100644
--- a/tests/validators.rs
+++ b/tests/validators.rs
@@ -39,9 +39,11 @@ fn test_validator_msg_newline() {
     assert!(res.is_err());
     let err = res.unwrap_err();
 
-    assert_eq!(
-        err.cause,
-        "Invalid value for \'<test>\': invalid digit found in string"
+    assert!(
+        err.to_string()
+            .contains("Invalid value for '<test>': invalid digit found in string"),
+        "{}",
+        err
     );
 
     // This message is the only thing that gets printed -- make sure it ends with a newline
diff --git a/tests/version.rs b/tests/version.rs
index a9a80f2f..cf302d5d 100644
--- a/tests/version.rs
+++ b/tests/version.rs
@@ -40,9 +40,7 @@ fn complex_version_output() {
     let _ = a.try_get_matches_from_mut(vec![""]);
 
     // Now we check the output of print_version()
-    let mut ver = vec![];
-    a.write_version(&mut ver).unwrap();
-    assert_eq!(str::from_utf8(&ver).unwrap(), VERSION);
+    assert_eq!(a.render_version(), VERSION);
 }
 
 #[test]