Skip to content

Replace unwrap() in doctests with ? #323

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 13, 2017

Conversation

Enet4
Copy link
Contributor

@Enet4 Enet4 commented May 5, 2017

Fixes #312.

  • All results yielding ParseError in the doctests should now be handled with the ? operator.
  • Some other specific results are unwrapped with expect() mapped to a placeholder string message, such as in URLs that are not cannot-be-a-base. Users are then expected to adjust this mapping depending on their use case. Once Replace () error types with specific types that implement Error #299 is resolved, we'll be able to propagate these normally.

This change is Reviewable

- `ParseError` is handled with the ? operator
- Some other specific results are unwrapped with `expect()`
@KiChjang
Copy link
Contributor

KiChjang commented May 5, 2017

Why are there so many # symbols added?

@Enet4
Copy link
Contributor Author

Enet4 commented May 5, 2017

@KiChjang They define lines of code that we want to consider in the tests but hide in the documented output. This is needed here to hide the boilerplate. The code needs to be wrapped in a function that returns a result in order to use the ? operator, but that should be implicit in the examples.

@budziq
Copy link

budziq commented May 9, 2017

There seam to be few unwrap calls left in the doctests immediately surrounding the change.
Also, would it not be beneficial to leave the run boilerplate at least once? As url does not provide convenience Result it was actually easier for me look into the source code to find the proper return type than analyze the docs in their current form.

@Enet4
Copy link
Contributor Author

Enet4 commented May 9, 2017

There seam to be few unwrap calls left in the doctests immediately surrounding the change.

Can you specify where? Note that lines hidden from the docs do not need to have their errors captured. Moreover, a good part of the unwraps still around are unwrapping Option, not Result.

Also, would it not be beneficial to leave the run boilerplate at least once? As url does not provide convenience Result it was actually easier for me look into the source code to find the proper return type than analyze the docs in their current form.

That would be debatable. Although a Result alias is not provided, the documented methods do show the error type ParseError, which is exported at the root of the crate. I suppose the crate could consider making the Result alias, although it's just a part of the equation, and not everyone agrees on the same naming conventions.

Copy link

@budziq budziq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Enet4

Can you specify where?

There are still dome unwrap() calls used in doctest for src/path_segments.rs

Moreover, a good part of the unwraps still around are unwrapping Option, not Result.

Well the point of the referenced lib-blitz issue #312 seams to be centered around promoting correct error handling in rust in general and unwrapping Option is no different from Result in this regard.

That would be debatable.

I completely agree. Just from the perspective of user who has tried to work with url yesterday for the first time I find current examples lacking and I guess that having Result alias would be preferable.

/// assert!(url.path_segments_mut().is_err());
///
/// let mut url = Url::parse("http://example.net/foo/index.html").unwrap();
/// let mut url = Url::parse("http://example.net/foo/index.html")?;
/// url.path_segments_mut().unwrap().pop().push("img").push("2/100%.png");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unwrap() still used in doctest

Copy link
Contributor Author

@Enet4 Enet4 May 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is something I would resolve in another PR: path_segments_mut returns Result<_,()>. With the API like this, we cannot capture it with the ? without mapping the error or rolling a custom error type. Note that using () as error type is not recommended by the Rust API guidelines (C-MEANINGFUL-ERR).

The same issue reproduces across your other mentions.

/// # use url::{Url, ParseError};
///
/// # fn run() -> Result<(), ParseError> {
/// let mut url = Url::parse("https://github.com/servo/rust-url/")?;
/// url.path_segments_mut().unwrap().clear().push("logout");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unwrap() still used in doctest

/// # use url::{Url, ParseError};
///
/// # fn run() -> Result<(), ParseError> {
/// let mut url = Url::parse("https://github.com/servo/rust-url/")?;
/// url.path_segments_mut().unwrap().push("pulls");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unwrap() still used in doctest

/// url.path_segments_mut().unwrap().push("pulls");
/// assert_eq!(url.as_str(), "https://github.com/servo/rust-url//pulls");
///
/// let mut url = Url::parse("https://github.com/servo/rust-url/").unwrap();
/// let mut url = Url::parse("https://github.com/servo/rust-url/")?;
/// url.path_segments_mut().unwrap().pop_if_empty().push("pulls");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unwrap() still used in doctest

/// # use url::{Url, ParseError};
///
/// # fn run() -> Result<(), ParseError> {
/// let mut url = Url::parse("https://github.com/")?;
/// let org = "servo";
/// let repo = "rust-url";
/// let issue_number = "188";
/// url.path_segments_mut().unwrap().extend(&[org, repo, "issues", issue_number]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unwrap() still used in doctest

/// # use url::{Url, ParseError};
///
/// # fn run() -> Result<(), ParseError> {
/// let mut url = Url::parse("https://github.com/servo")?;
/// url.path_segments_mut().unwrap().extend(&["..", "rust-url", ".", "pulls"]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unwrap() still used in doctest

@Enet4
Copy link
Contributor Author

Enet4 commented May 9, 2017

For the reasons stated above, I suggest that #299 is resolved before those unwraps are replaced.

@dtolnay
Copy link
Contributor

dtolnay commented May 12, 2017

Yes the () errors make error handling obnoxious. We would all love a fix for #299.

But as it is, unwrap() is not the way you would handle these in real code. I think the examples should show realistic error handling even if it is awkward, because people will paste these examples into their own code.

@Enet4
Copy link
Contributor Author

Enet4 commented May 12, 2017

Ok, I will look into them today, possibly devising an error type for the purpose. I also wish to help addressing #299, since it appears we could highly benefit from better error handling.

Update: All right, all unwraps should be gone. I also found a try! in another function's doctests, which I replaced with ?.

- Add `CannotBeBaseError` struct to examples involving cannot-be-a-base URLs, map unit errors to this type;
- Replace previous `path_segment().expect()` with `CannotBeBaseError` mapping;
- replace try! with ? in url.with_defaul_port example.
@dtolnay
Copy link
Contributor

dtolnay commented May 12, 2017

Thanks! That eliminates the remainder of the unwraps.

Some of these have a bit more error-handling noise than I would find valuable. For example the following one from PathSegmentsMut::clear is 12 lines of error handling to 4 lines of example code.

use url::Url;
use std::error::Error;
use std::fmt;

#[derive(Debug)]
struct CannotBeBaseError;
impl fmt::Display for CannotBeBaseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.description())
    }
}
impl Error for CannotBeBaseError {
    fn description(&self) -> &str { "cannot be a base" }
}

let mut url = Url::parse("https://github.com/servo/rust-url/")?;
url.path_segments_mut().map_err(|_| CannotBeBaseError)?
    .clear().push("logout");
assert_eq!(url.as_str(), "https://github.com/logout");

Since Box<Error> implements From<&str>, what do you think of the following? In contrast to unwrap, this still leaves the user an obvious place to plug in their own error handling after they copy+paste this code. They can't forget to do it like they probably would with unwrap.

use url::Url;

let mut url = Url::parse("https://github.com/servo/rust-url/")?;
url.path_segments_mut().map_err(|_| "cannot be a base")?
    .clear().push("logout");
assert_eq!(url.as_str(), "https://github.com/logout");

@Enet4
Copy link
Contributor Author

Enet4 commented May 12, 2017

I agree that the current approach is verbose (not to mention that it definitely looks like something that should have been an error variant in the first place!). I'll follow the advice of using &'static str as a placeholder error and push a commit right away.

- Remove verbose `CannotBeBase` error in examples, use a `&'static str` instead.
Copy link
Contributor

@dtolnay dtolnay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work. Thanks for sticking with this!

Copy link

@bjorn3 bjorn3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run is never called in many doctests, so they are not tested

@@ -44,6 +45,8 @@ assert!(issue_list_url.path_segments().map(|c| c.collect::<Vec<_>>()) ==
assert!(issue_list_url.query() == Some("labels=E-easy&state=open"));
assert!(issue_list_url.fragment() == None);
assert!(!issue_list_url.cannot_be_a_base());
# Ok(())
# }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fn run is not called


let data_url = Url::parse("data:text/plain,Hello?World#").unwrap();
# fn run() -> Result<(), ParseError> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

- add `run().unwrap()` to all result-guarded doctests
@Enet4
Copy link
Contributor Author

Enet4 commented May 16, 2017

@bjorn3 Oops, you are right! I appended calls to run() in the latest commit.

@SimonSapin
Copy link
Member

This looks great. Thanks all!

@bors-servo r+

@bors-servo
Copy link
Contributor

📌 Commit c58678a has been approved by SimonSapin

@bors-servo
Copy link
Contributor

⌛ Testing commit c58678a with merge 923c02f...

bors-servo pushed a commit that referenced this pull request Jun 13, 2017
Replace unwrap() in doctests with `?`

Fixes #312.
 - All results yielding `ParseError` in the doctests should now be handled with the ? operator.
 - Some other specific results are ~unwrapped with `expect()`~ mapped to a placeholder string message, such as in URLs that are not _cannot-be-a-base_. Users are then expected to adjust this mapping depending on their use case. Once #299 is resolved, we'll be able to propagate these normally.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/rust-url/323)
<!-- Reviewable:end -->
@bors-servo
Copy link
Contributor

☀️ Test successful - status-travis
Approved by: SimonSapin
Pushing 923c02f to master...

@bors-servo bors-servo merged commit c58678a into servo:master Jun 13, 2017
@Enet4 Enet4 deleted the imp/no-unwrap-in-doctests branch June 13, 2017 14:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Modify examples to not call unwrap
7 participants