Create io::util::IteratorExtension to provide fail_on_error() and update io iterators to produce IoResults

Most IO related functions return an IoResult so that the caller can handle failure
in whatever way is appropriate. However, the `lines`, `bytes`, and `chars` iterators all
supress errors. This means that code that needs to handle errors can't use any of these
iterators. All three of these iterators were updated to produce IoResults.

The problem with returning IoResults is that the caller now *has* to handle error conditions
since the produced value is now wrapped in an IoResult. This complicates simple example
code or one-off programs. In order to mitigate that problem, a new extension trait,
io::util::IteratorExtension, is created that provides a new method for all iterators over
IoResults - fail_on_error(). This method wraps the iterator in a FailOnError iterator that
unwraps all results before producing them, but fails if a non-EndOfFile error is
encountered. This allows for simple programs to remain almost as simple as before.

With these changes, the existing behavior of supressing all errors for these iterators is
effectively no longer available.

Fixes #12368
Add result::IteratorExtension to provide stop_on_error()

The stop_on_error() extension method wraps an iterator over Results so that
unwrapped values are produced until the first Error value is encountered. This
effectively restores the ability to use the `lines`, `bytes`, and `chars` iterators
so that they continue iteration until the first Error, but do not fail!().