HN user

shepmaster

1,167 karma

Cofounder of the world's first Rust consultancy[1] — we are available to help you and your company with any and everything related to Rust! We also produce the Rust in Motion video series[2] for Manning.

[1]: http://integer32.com/ [2]: https://www.manning.com/livevideo/rust-in-motion?a_aid=jgoulding&a_bid=6a993c2e

Posts26
Comments269
View on HN
seri.tools 8mo ago

Why Castrol Honda Superbike crashes on (most) modern systems

shepmaster
176pts35
fly.io 1y ago

Parking_lot: Ffffffffffffffff

shepmaster
7pts3
arstechnica.com 1y ago

Torvalds: You can avoid Rust as a C maintainer, but you can't interfere with it

shepmaster
203pts188
josh-project.github.io 3y ago

Just One Single History – combine the advantages of monorepos with multirepos

shepmaster
1pts0
foundation.rust-lang.org 3y ago

Rust Foundation – Introducing Our Newest Project Grantees

shepmaster
8pts0
stackoverflow.com 6y ago

Why does Rust not optimize code assuming that mutable references cannot alias?

shepmaster
1pts0
www.youtube.com 7y ago

Rust: A Language for the Next 40 Years – Carol Nichols

shepmaster
5pts1
www.youtube.com 7y ago

Videos from Rust Belt Rust 2018 are now available

shepmaster
2pts0
rust-belt-rust.com 8y ago

Rust Belt Rust 2017 Sessions Announced

shepmaster
6pts0
www.youtube.com 9y ago

Videos from Rust Belt Rust 2016 are now available

shepmaster
6pts0
www.integer32.com 10y ago

Why we're starting a Rust consultancy

shepmaster
4pts1
www.rust-belt-rust.com 10y ago

Rust Belt Rust Conference session abstracts are now available

shepmaster
5pts1
jakegoulding.com 11y ago

The Rust FFI Omnibus – Soliciting Suggestions and Feedback

shepmaster
4pts1
github.com 12y ago

Rock, Paper, Scissors in Clojure using core.typed and core.async

shepmaster
1pts0
github.com 12y ago

Type-directed TDD in Rust

shepmaster
2pts0
alistapart.com 12y ago

Accessibility: The Missing Ingredient

shepmaster
4pts0
jakegoulding.com 13y ago

A little dip into Ruby's Marshal format (Part 1 / 3)

shepmaster
1pts0
jakegoulding.com 13y ago

Refactor and make changes in different commits

shepmaster
5pts0
jakegoulding.com 13y ago

Finding a race condition in Capybara with Selenium

shepmaster
4pts1
jakegoulding.com 14y ago

The stages of code review

shepmaster
1pts0
jakegoulding.com 14y ago

Test double terminology - The misuse of the term "mock"

shepmaster
1pts0
jakegoulding.com 14y ago

When refactoring isn't refactoring

shepmaster
5pts0
andrewcox.org 14y ago

Coderetreat: A first-time facilitator’s view

shepmaster
1pts0
jakegoulding.com 14y ago

What I've learned about testing over the last year

shepmaster
3pts0
jakegoulding.posterous.com 15y ago

How mock objects make Gantt charts (more) useless

shepmaster
15pts10
jakegoulding.posterous.com 15y ago

SQLite, 64-bit integers, and the impossible number

shepmaster
2pts0

Haven't used --update-refs, but reading it, it should result in your third graph.

I don't think it does. I tried locally:

  mkdir test; cd test; git init
  touch a; git add a; git commit -m 'a'
  touch b; git add b; git commit -m 'b'
  touch c; git add c; git commit -m 'c'
  git checkout -b branched-feature HEAD~
  touch d; git add d; git commit -m 'd'
  git checkout main
  echo 'change' > b; git add b; git commit -m 'fix b'
  git rebase -i --root --update-refs
And ended up with this graph (`git log --graph --all`)
  * commit (HEAD -> main)
  |
  |     c
  |
  * commit 
  |
  |     b
  |
  | * commit (branched-feature)
  | |
  | |     d
  | |
  | * commit
  |/  
  |       b
  |
  * commit 
  
        a
Replacing the `git commit -m 'fix b'; git rebase -i --root --update-refs` with `git history fixup HEAD~` produces what I'd like:
  * commit (branched-feature)
  |
  |     d
  |
  | * commit (HEAD -> main)
  |/
  |       c
  |
  * commit
  |
  |     b
  |
  * commit 
  
        a

I'm not sure that answers my question. That shows a linear set of branches (my-feature-v3 depends on my-feature-v2 depends on my-feature-v1 depends on main). I'm asking about the case where two or more branches fork from a common ancestor and you want to fix the common ancestor.

That last part goes further than git rebase --update-refs, which only moves refs sitting inside the range you’re actively rebasing. git history instead finds and rewrites every local branch descended from the commit (while also having an option to limit it to only the current branch).

I'm reading that to mean that when I use `git rebase --update-refs` in this situation, where I've currently checked out `D` and update `B` to `B'`:

  A ──► B ──► C ──► D  
        │              
        └───► E        
I'll end up with this state, where `E` remains untouched?
  A ──► B' ─► C' ─► D'  
  │                   
  └───► B ──► E        
(EDIT: Originally I had `E` point to `B'`, which doesn't make sense)

If I use `git history fixup`, it would also update `E` and end up with this?

  A ──► B' ─► C' ─► D'  
        │              
        └───► E'        
If that's the case, is there a way to get `git rebase` to have the same behavior? I've got decades of `git rebase` burned into my fingers at this point.

You certainly can use thiserror to accomplish the same goals! However, your example does a little subtle slight-of-hand that you probably didn't mean to and leaves off the enum name (or the `use` statement):

    low_level_result.context(ErrorWithContextSnafu { context })?;
    low_level_result.map_err(|err| CustomError::ErrorWithContext { err, context })?;
Other small details:

- You don't need to move the inner error yourself.

- You don't need to use a closure, which saves a few characters. This is even true in cases where you have a reference and want to store the owned value in the error:

    #[derive(Debug, Snafu)]
    struct DemoError { source: std::io::Error, filename: PathBuf }

    let filename: &Path = todo!();
    result.context(OpenFileSnafu { filename })?; // `context` will change `&Path` to `PathBuf`
- You can choose to capture certain values implicitly, such as a source file location, a backtrace, or your own custom data (the current time, a global-ish request ID, etc.)

----

As an aside:

    #[error("failed to open a: {0}")]
It is now discouraged to include the text of the inner error in the `Display` of the wrapping error. Including it leads to duplicated data when printing out chains of errors in a nicer / structured manner. SNAFU has a few types that work to undo this duplication, but it's better to avoid it in the first place.

In addition to the sibling comment mentioning thiserror, I also submit my crate SNAFU (linked in my ancestor comment). Reducing some of the boilerplate is a big reason I enjoy using it.

I create one error type per function/action

I do too! I've been debating whether I should update SNAFU's philosophy page [1] to mention this explicitly, and I think your comment is the one that put me over the edge for "yes" (along with a few child comments). Right now, it simply says "error types that are scoped to that module", but I no longer think that's strong enough.

[1] https://docs.rs/snafu/latest/snafu/guide/philosophy/index.ht...

SNAFU author here, thanks for including my crate! I’ll try to give your review a thorough read through later and incorporate feedback that makes sense.

I do have https://diataxis.fr/ and related stuff open in another tab and keep meaning to figure out how to best apply it for SNAFU.

Out of curiosity, do you recall if you also read the top-level docs[1]? That’s intended to be the main introduction, I actually don’t expect most people to read the user’s guide, unfortunately.

[1] https://docs.rs/snafu/latest/snafu/index.html

I agree. Unfortunately, I think that a lot of the people who ask for a bigger standard library really just want (a) someone else to do the work (b) someone they can trust.

The people working on Rust are a finite (probably overextended!) set of people and you can't just add more work to their plate. "Just" making the standard library bigger is probably a non-starter.

I think it'd be great if some group of people took up the very hard work to curate a set of crates that everyone would use and provide a nice façade to them, completely outside of the Rust team umbrella. Then people can start using this Katamari crate to prove out the usefulness of it.

However, many people wouldn't use it. I wouldn't because I simply don't care and am happy adding my dependencies one-by-one with minimal feature sets. Others wouldn't because it doesn't have the mystical blessing/seal-of-approval of the Rust team.

I agree with your general point, but for this specific functionality, I’ll point out that setting environment variables of the current process is unsafe. It took us a long time to realize it so the function wasn’t actually marked as unsafe until the Rust 2024 edition.

What this means in practice is that the call to invoke dotenv should also be marked as unsafe so that the invoker can ensure safety by placing it at the right place.

If no one is maintaining the crate, that won’t happen and someone might try to load environment variables at a bad time.

[Rust and C++] encourages creating a library of types with inheritance

You'll want to expand on and clarify your meaning here, as Rust does not have inheritance.

Yep, as the sibling said, it’s to prove the point in a snarky manner. The yellow-on-white color, the wobbling, upside down path of text, the font choice, the unrelated image; all of these serve to make the concept actively hard to process.

Hopefully this sparks a little “wow I wonder if my image of text was also hard to read like I just experienced” moment.

Agree with simonw’s sibling comment. To add to it, I’m the primary maintainer of the Rust playground and basically self-review every single commit.

The rust-lang/rust repository has higher scrutiny (in part driven by tools like bors, the subject of the article).

This is one of the things I like about SNAFU: it makes this preferred pattern the default and makes it nicer to use. For example, your usage would look something like this with SNAFU:

    create_dir(path).context(CreateStagingDirectorySnafu { path })?;
Note a few points:

1. No need to use the closure

2. No need to carry the source error over yourself (`context` does this for you)

3. No need to explicitly call `clone` on the path (`context` does this for you)

When presented with a bug from the field, I also care about finding the path through my code where it occurred, but rarely do I need to know that `foo` called `foo_with_caching` called `foo_with_caching_recursive`. When reading a backtrace, I skip over amounts of "implementation details" to get a big picture. For me, the exact functions / files / line numbers are not relevant, doubly so if I'm working in a situation where the error message isn't tied to a specific git commit and the functions/files/lines have moved over time.

To reiterate my point from above though, my error stacks are all unique — seeing the stack will point me to an exact line in my code where the error occurred, even though I don't include function/file/line as-is.

I don't think you need a lint. When you define the error type returned by `handle_request`, you decide how the error type returned by `handle_request` will be incorporated. If you've decided to implement `From` then you've decided you don't want/need to add context. Otherwise, the compiler will give you an error when you use `?`.

The time I can think this won't work is when you are reusing error types across places. Recently, I've been experimenting with creating a lot of error types, so far as one unique error type per function. I haven't done this for long enough to have a real report, but I haven't hated it so far.

Without deeply looking into it, I'd expect that to integrate with SNAFU, you could basically write something like this:

    struct SpanTraceWrapper(tracing_error::SpanTrace);
    
    impl snafu::GenerateImplicitData for SpanTraceWrapper {
        fn generate() -> Self {
            Self(tracing_error::SpanTrace::capture())
        }
    }
And then you can use it as
    #[derive(Debug, Snafu)]
    struct SomeError {
        #[snafu(implicit)]
        span_trace: SpanTraceWrapper,
    }
This will capture the `SpanTrace` whenever `SomeError` is constructed (e.g. `thing().context(SomeSnafu)` or `SomeSnafu.fail()`.