[0]: https://docs.rs/snafu/latest/snafu/derive.Snafu.html#disabli...
[1]: https://docs.rs/snafu/latest/snafu/derive.Snafu.html#delegat...
1,167 karma · joined February 6, 2011
[1]: http://integer32.com/ [2]: https://www.manning.com/livevideo/rust-in-motion?a_aid=jgoulding&a_bid=6a993c2e
[0]: https://docs.rs/snafu/latest/snafu/derive.Snafu.html#disabli...
[1]: https://docs.rs/snafu/latest/snafu/derive.Snafu.html#delegat...
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.
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...
I’m glad to see SNAFU was useful to others!
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
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.
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...
> 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...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...
For example, the standard library `String` type dereferences to `str` which you can then re-reference to `&str`.
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.
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.
https://doc.rust-lang.org/std/backtrace/struct.Backtrace.htm...
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.
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.
> 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.
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.
What’s so different about this way of making peanut butter that results in no separation?
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_Protocolvs
initial
Date: 2017-12-21 22:25:45 +0300
— https://github.com/rust-lang/rust-analyzer/commit/a63222cd24... read_to_string(path).context(ConfigFileSnafu { path })?;
[SNAFU]: https://docs.rs/snafu/latest/snafu/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.
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
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.
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.
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.