HNHacker News
TopNewBestAskShowJobs

shepmaster

1,167 karma · joined February 6, 2011

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

submissionscomments
shepmaster··on Error Stacking in Rust
SNAFU follows much the same idea: we have an attribute you can add [0] when you want to allow directly implementing `From`. Like thiserror, you can also mark an error as transparent [1] when even the error existing doesn't provide useful information.

[0]: https://docs.rs/snafu/latest/snafu/derive.Snafu.html#disabli...

[1]: https://docs.rs/snafu/latest/snafu/derive.Snafu.html#delegat...

shepmaster··on Error Stacking in Rust
> pushing humans a little more to care about errors

This is 100% a reason that I like using SNAFU. The term I use for this is a "semantic stack trace" — a lot of the time, the person experiencing the error doesn't care that it occurred in "foo.rs" or "fn bar()" or "line 123". Instead, they care what the program is trying to do ("open the configuration file", "download the update file").

When I'm putting effort into my errors, I basically never use `snafu::Location` or `snafu::Backtrace`. My error stacks should always be unique — any stack can exactly point to a trace through my program.

shepmaster··on Error Stacking in Rust
It can't be used as a literal build dependency [0], no. However, the fact that your crates uses SNAFU should [1] be completely hidden from your users. From the outside, you just return a regular enum or struct as your error type. If you were to look at the symbols in the resulting binary, I would expect that you could see references to the trait method `snafu::ResultExt::context` (and similar functions across similar types) depending on how well the code was inlined. If you use other features like `snafu::Location` or `snafu::Report`, those would definitely show up.

You don't have to use the macros, no. When you define your error type, you can mark a field as `#[snafu(implicit)]` [2]. When the error is generated, that field will be implicitly generated via a trait method. The two types this is available for are backtraces and locations, but you could create your own implementations such as grabbing the current timestamp or a HTTP request ID.

[0]: https://doc.rust-lang.org/cargo/reference/specifying-depende...

[1]: There's one tiny leak I'm aware of, which is that your error type will implement the `snafu::ErrorCompat` trait, which is just a light polyfill for some features not present on the standard library's `Error` trait. It's a slow-burn goal to remove this at some point, likely when the error "provider API" stabilizes.

[2]: https://docs.rs/snafu/latest/snafu/derive.Snafu.html#control...

shepmaster··on Error Stacking in Rust
Hey all, I’m the author of SNAFU (mentioned in the article). I’m off to bed now, but I’d be happy to try and answer any questions people might have sometime tomorrow.

I’m glad to see SNAFU was useful to others!

shepmaster··on Async Rust in Three Parts
See my sibling comment [1] for my current fun bug hunt. Overall, it's all pretty stable until I (try to) make it do new things.

[1]: https://news.ycombinator.com/item?id=41931288

shepmaster··on Async Rust in Three Parts
> How many users does it serve?

I don't really track users, but over the last 24 hours, there were 47.8k meaningful [1] requests taking a total of 28.2 hours. That ~0.5 requests per second number has been relatively consistent.

> re-use already compiled binaries of all supported dependencies and only need to compile the user's code and link it, but is there other clever strategies?

Yes, we pre-compile all the available dependencies [2] and that's about it.

> I use it all the time with no performance issues

That's good to hear! There's been a long-running bug where the playground binary loses track of the child Docker container (maybe?) and then the machine runs out of memory and the OOM killer often does more harm than good [3]. While trying to pin that down, I've recently caused the entire process to get into what appears to be a complete deadlock where no requests can be serviced at all. This tends to happen while I'm asleep so either I have no chance to debug it before it is auto-killed or the playground is unresponsive for 8+ hours.

[1]: compiling / executing code, running clippy/miri/rustfmt, expanding macros

[2]: https://github.com/rust-lang/rust-playground/blob/c4d00b90aa...

[3]: somehow it does something that ends up killing the network stack and then the machine is basically dead in the water. Very similar to what is reported in https://serverfault.com/q/1125634/119136

shepmaster··on Async Rust in Three Parts
> the Rust Playground lets you run a hundred threads

It's more that we don't do anything to prevent it, other than coarse process-wide memory / CPU time limits. IIRC, Rust-spawned threads on Linux use 2MiB of stack space by default, so that seems like a likely cap.

Note that the playground is only 2 cores and you are sharing with everyone else, so you aren't likely to really benefit.

shepmaster··on Code Generation in Rust vs. C++26
Code-wise, it's not too painful [1], the problem is that you need to enable more features for syn. By default, syn doesn't compile in support for parsing arbitrary types / expressions, which does increase the time / space needed.

Since syn is a pretty fundamental crate, I've a feeling that Serde probably doesn't want to turn on this feature for minimal gain, but that's pure speculation on my part.

[1]: https://github.com/shepmaster/snafu/blob/1dbba9514e2abfdff01...

[2]: https://github.com/shepmaster/snafu/blob/1dbba9514e2abfdff01...

shepmaster··on Code Generation in Rust vs. C++26
In case the author of the article is reading...

> This is because Rust’s attribute grammar can’t support a callable here.

The grammar of attributes supports it fine, it's just that Serde chooses to not use it. I'm not sure if it's because Serde started before it was allowed or if it's a stylistic preference or what.

For example, my crate SNAFU allows you[1] to use attributes containing types (`Error`) and expressions (`Box::new`):

    #[derive(Debug, Snafu)]
    #[snafu(source(from(Error, Box::new)))]
    struct ApiError(Box<Error>);

[1]: https://docs.rs/snafu/latest/snafu/derive.Snafu.html#transfo...
shepmaster··on Guide to Error Handling in Rust
I think you are asking for this config [0] (let me know if not, maybe provide an example), but I’d caution against renaming it back to “Error”. The context selectors are not themselves errors, they are more-or-less fancy error constructors. It’s the same logic I’d use to call it `Error::new` instead of `Error::error`.

Having the context selectors called the exact same as the variants was the way the original version of SNAFU worked, but it was also the number one cause of confusion and support requests.

[0]: https://docs.rs/snafu/latest/snafu/derive.Snafu.html#changin...

shepmaster··on Guide to Error Handling in Rust
I don’t know the dbus crate, so I’m not trying to say anything about that crate’s usage, I’m simply saying that referencing after dereferencing is idiomatic Rust.

For example, the standard library `String` type dereferences to `str` which you can then re-reference to `&str`.

shepmaster··on Guide to Error Handling in Rust
I partially agree. I have been experimenting with creating a lot of small error types (e.g. one per function) because of the benefit of narrowing down the set of possible causes. I also dislike crate-wide errors for the same reason.

However, having an anonymous sum type isn’t a good solution here because there’s nowhere for you to add context to. It’s not useful for your final error to say “permission denied” without the context of a file name, or knowing that you were trying to open configuration file, etc.

Also, depending on your implementation, you either cannot allow multiple errors of the same underlying cause (e.g. two IO errors, one for writing and one for opening) because they are the same type or you have positional error tuples (error index 0 is write, index 1 is open). Neither of those is super ergonomic.

shepmaster··on Guide to Error Handling in Rust
Yes, `&*foo` is meaningful. For example, `Vec<T>` becomes `[T]` and then `&[T]`.

Yes, a `Box<dyn Error>` is about the most rudimentary way of returning an arbitrary error, but it’s better than a panic or a sentinel value.

shepmaster··on Guide to Error Handling in Rust
Do note that the standard library has stabilized backtraces now, so you only need that if you are targeting an older version of Rust.

https://doc.rust-lang.org/std/backtrace/struct.Backtrace.htm...

shepmaster··on Guide to Error Handling in Rust
The biggest thing to me is that many people lump errors by type (“all IO errors are equivalent”) which is very wrong to me. SNAFU encourages and streamlines bucketing your errors into detailed chunks.

Along the way, it makes adding details to your error easier (automatic calls to Into without a closure; optional implicit details).

It combines string-like errors (anyhow) and structured errors (thiserror) in one library.

It has basic command line error formatting that shows the causal chain without duplication.

I don’t use eyre so I don’t feel comfortable comparing to it.

shepmaster··on Guide to Error Handling in Rust
For that haven’t seen it, I’ll drop in my library SNAFU

https://docs.rs/snafu/latest/snafu/

shepmaster··on Show HN: My 70 year old grandma is learning to code and made a word game
I hope Grandma has some analytics hooked up to see all the traffic coming in!

A small bug report: the "Share score" button on macOS Safari doesn't offer "copy" as an option for some reason. The same functionality on guess the game[0] has the same limitation, but it also copies to the clipboard automatically. Wordle[1] seems to just copy to the clipboard only.

[0]: https://guessthe.game

[1]: https://www.nytimes.com/games/wordle/index.html

shepmaster··on Rust Just Failed an Important Test
>> I often want to make public API changes to a library I maintain

> They have such a tool, its called crater

There are tools out in the wild that help crate authors do similar things. For example, I wrote my own[0] to evaluate some breaking changes for SNAFU. There's also crater-at-home[1] which I don't have any direct experience with.

[0]: https://github.com/shepmaster/mini-crater

[1]: https://github.com/saethlin/crater-at-home

shepmaster··on Rust Just Failed an Important Test
(Rust playground maintainer here) Incidentally, I also updated the Rust playground today. My usual process is to update everything [0] that is SemVer compatible and then one-by-one update packages with SemVer incompatible version bumps.

During the upgrade, I was thinking to myself how generally I’m fairly blasé about these updates — I don’t even attempt to build the code with the SemVer compatible updates because it’s so rare for a crate maintainer to stuff up such an update. The SemVer incompatible updates get a build just as a sanity check.

Contrast this to the JS dependencies where an upgrade tends to instill more dread in me. Part of this is a lack of confidence in my test suite, part of it is that it’s the more UI heavy component which is hard to write tests for in the first place, but a non-zero part is my lack of trust in the JS libraries and tooling. I wonder how much of that is still true today versus how much I am once bitten, twice shy.

[0]: https://github.com/rust-lang/rust-playground/pull/1079

shepmaster··on Show HN: Peanut Butter Spinner
Many recipes for Vitamix peanut butter also mention mixing in some ice cream or something similar once you’ve scraped out as much peanut butter as you can. Then you get a little peanut butter smoothie as a treat at the end and help get out some of the extra peanut butter.
shepmaster··on Show HN: Peanut Butter Spinner
That’s what I came here to ask. When I make peanut butter in the Vitamix, I roast the peanuts and then blend them. I’ve never experienced separation, although I do store it in the refrigerator.

What’s so different about this way of making peanut butter that results in no separation?

shepmaster··on Higher RAII and the seven arcane uses of linear types
A year and a half doesn't seem like bad turnaround time to me. shrug

    On June 27, 2016, Microsoft announced a collaboration with Red Hat and Codenvy to standardize [Language Server Protocol]'s specification
— https://en.wikipedia.org/wiki/Language_Server_Protocol

vs

    initial
    Date:   2017-12-21 22:25:45 +0300
— https://github.com/rust-lang/rust-analyzer/commit/a63222cd24...
shepmaster··on rustup in official apt repositories: starting from Debian 13 and Ubuntu 24.04
Standalone installers for Rust: https://forge.rust-lang.org/infra/other-installation-methods...
shepmaster··on Emacs-copilot: Large language model code completion for Emacs
What is the upgrade path for a Llamafile? Based on my quick reading and fuzzy understanding, it smushes llama.cpp (smallish, updated frequently) and the model weights (large, updated infrequently) into a single thing. Is it expected that I will need to re-download multiple gigabytes of unchanged models when there's a fix to llama.cpp that I wish to have?
shepmaster··on Banging errors in Go
Rust libraries like my [SNAFU] allow you to both add additional details and use the `?` operator:

    read_to_string(path).context(ConfigFileSnafu { path })?;
[SNAFU]: https://docs.rs/snafu/latest/snafu/
shepmaster··on We made a fork of the Rust Playground
> Its not using the whole orchestrator...thing

For further context, the "orchestrator" is a reimplementation of some of the backend.

The original version of the playground basically took the user input, dumped it into a file, then mounted that file into a Docker container and waited for it to be complete — a traditional batch process. This works great, but doesn't allow fun things like streaming input / output from the process, or having temporary files that persist over a short time period.

The new code has a shim program that lives inside the container and we can communicate with it via messages passed on stdin/stdout. Things are a lot more asynchronous and a bit more complicated.

I think it's a reasonable thing to avoid for now, but note that my plan is to eventually remove the current synchronous code eventually.

> I need to manually update download urls every now and then

I do much the same, roughly every 6 weeks or so.

> a big enough machine

The primary instance is a single c5a.large in EC2. I also run my own instance on a single t2.micro.

shepmaster··on We made a fork of the Rust Playground
I think that yes, it's technically possible with a good amount of work at all layers of the stack. The bigger worry I'd have is purely about time. The Docker images for the Rust playground clock in close to 2 GiB, and there's 3-6 of those. No one is going to wait to download that to run a small program.

Now, because no one has put in all the work to make it happen, I don't know if this would be a real problem or not, but I haven't yet heard a comprehensive counter argument.

Some related ideas:

- Client-side interactive terminal for WASM https://github.com/rust-lang/rust-playground/issues/374

- Compile Rust Language Server into WASM https://github.com/rust-lang/rust-playground/issues/357

shepmaster··on We made a fork of the Rust Playground
Primary maintainer of the Rust Playground here. I'm glad to see open source doing the open source thing!

I'll try to answer any questions from the Rust point of view, and it will be good to hear of any large differences between the two implementations.

shepmaster··on Adding runtime benchmarks to the Rust compiler benchmark suite
> used as complements to time-based profilers, not to replace them

Sure. I also use hyperfine to run a bigger test as a user would see the system. I cross reference that with the instruction counts. I use these hardware metrics in a free CI runner, and hyperfine locally.

shepmaster··on Adding runtime benchmarks to the Rust compiler benchmark suite
> With cloud CI runners you'd still have issues with hardware differences

For my project it really is the diff of each commit, which means that I start from a parent commit that isn’t part of the PR and re-measure that, then for each new commit. This should avoid accounting for changes in hardware as well as things like Rust versions (if those aren’t locked in via rustup).

The rest of your points are valid of course, but this was a good compromise for my OSS project where I don’t wish to spend extra money.

← PreviousPage 2 of 9Next →