HNHacker News
TopNewBestAskShowJobs

duckerude

1,881 karma · joined December 10, 2017

submissionscomments
duckerude··on Rust 1.63
The original implementation gave out a handle that waited for all the threads to end when it was dropped (i.e. went out of scope). That would prevent premature cleanup. But it was possible to leak the handle so that it would never be dropped even at the end of the block, and then it wouldn't wait for the threads.

The new implementation calls a closure and waits for the threads when that closure returns. Unlike the destructor here's no way to stop that code from running when it should.

duckerude··on Building a Rust Mentality
You're getting flak for this, but you have a point.

I wouldn't say it changes very fast. If you look at the language features in the past two releases they're all of the form "allow {existing feature} to do {thing you'd expect to be allowed}", and the library additions are minor.

But it changes often, every six weeks, where e.g. Python releases features only once a year. And it's common practice to install new releases as soon as they come out without waiting for distros to pick them up. That means software starts depending on new features in minor frivolous ways that nevertheless break the build for older Rust versions.

I make an effort to have my libraries support older Rust releases, but I have to go out of my way to check for compatibility. Not everybody does that, and it's unrealistic to expect everyone to—but with a different release schedule that would give fewer issues.

duckerude··on GCC Rust Approved by GCC Steering Committee
Does it? GCC's Java frontend died and is no longer shipped, they need maintainers like any other compiler.
duckerude··on What's New in Python 3.11?
That problem also exists in sets because some values that aren't interchangeable still compare equal:

  >>> {1} | {1.0} | {True}
  {1}
But the behavior for dicts is surprising even in that light:

  >>> {1: 1} | {1.0: 1.0} | {True: True}
  {1: True}
duckerude··on What it feels like when Rust saves your bacon
I've found it very easy to get started with fuzzing in Rust using cargo-fuzz. I didn't do anything very advanced, and my closest point of reference is testing Python with Hypothesis, but it did turn up bugs.

Here's a Rust fuzzing story from yesterday: https://hacks.mozilla.org/2022/06/fuzzing-rust-minidump-for-...

It claims that Rust is particularly suitable for it because integer overflow panics in debug builds (and out of bounds indexing always panics), which sounds reasonable.

duckerude··on What it feels like when Rust saves your bacon
Some people make the opposite claim: Rust lets you get away with less dynamic allocation (and more data shared between threads) because you can rely on the compiler checking your work.

The effect might be positive or negative depending on the circumstances.

duckerude··on Python 3.11 beta vs. 3.10 benchmark
It can, but it doesn't have a "do reasonable things" mode. Instead it notices when you do something unreasonable and undoes its optimizations.
duckerude··on Python 3.11 beta vs. 3.10 benchmark
> just like in every other mainstream language

JavaScript doesn't do this, you can replace properties of window at will. I don't think Ruby does it, or Lua. PHP probably does it under some circumstances in modern versions but only because it's remarkably un-dynamic for an interpreted language.

This is normal fare for high-level interpreted languages, for better or worse.

duckerude··on Bugs that the Rust compiler catches for you
The oldest rustc version I target (for a tiny project, admittedly) is over 6 years old, and it's fine. When code works it also works on the latest release. When code doesn't work I get a reasonable compile-time error message, not a mysterious segfault at runtime.

It's not easy, even in a few hundred lines I had to remove many conveniences to go that far back, but it's very legible. The compiler has my back, I'm never confused about whose fault it is.

Rust is young, but I don't expect this to be any different in 2030.

duckerude··on I want off Mr. Golang’s Wild Ride (2020)
I don't think this article intends to give a full appraisal of Go. It highlights the downsides of a particular philosophical stance Go takes.

That stance is very much real, even if it's best described by gesturing at half a dozen examples. And it's nothing new, really. It's just Worse is Better all over again. I think it's instructive to see how it plays out in Go.

This rant is one-sided, but it doesn't pretend to be otherwise.

duckerude··on The Hare programming language
I don't know if the world needs this language, but I'm glad it exists. There's a real charm to C's straightforwardness. It deserves to be expressed without broken array handling and a clunky preprocessor and overzealous UB and so on.

I use Rust and like it well enough but it's amazing that its amount of complexity can work at all. It's healthy to have a counterpoint that stays clear even of generics.

duckerude··on Rustaceans at the border
> the kernel is a service that end user applications interact with through an API.

My thinking is that that API has to distinguish between many strictly defined error codes, while anyhow homogenizes errors and focuses on human-readable information. How would you tell whether an anyhow::Error means EINVAL or EFAULT?

It's the same fundamental design tradeoff that makes it unsuitable for Rust libraries, even if anyhow wouldn't be visible in the public API.

> The uuid crate can be modified to support the kernel infrastructure as its backend

Fair enough, using a modified version seems reasonable.

duckerude··on Rustaceans at the border
anyhow is for careless error propagation, where you know you won't do elaborate handling and just bubble it up to the user. It's suitable for (some) applications, but not for libraries, and a kernel falls on the "library" side of that divide. Errors should be handled by the kernel itself or reported carefully to userspace. anyhow is not built for that. (It also puts its errors on the heap—what do you do if memory allocation fails when reporting an error?)

Linux has its own UUID infrastructure, so it seems weird to pull in a whole parallel implementation. At the very least you'd want to disable all the generation code, and it's unclear to me if the uuid crate supports that.

rand is large. Does Linux need all the probability distributions it supports? (ripgrepping for "Bernoulli" only turns up the name of a floppy disk system, so probably not.) Doesn't it already have an implementation for the ones it does need?

duckerude··on Rustaceans at the border
That's true when running `cargo install` to install an application directly from crates.io, but not when running `cargo build` in an already checked-out repository.
duckerude··on Nim vs Rust Benchmarks
For the spectral-norm one I think it's a failed attempt at parallelism. This code spawns 4000 tasks:

    parallel:
      for i in 0..<n:
        spawn times_i(v, u, i, n, reverse)
(https://github.com/hanabi1224/Programming-Language-Benchmark...)

It's almost four times as slow as the otherwise identical single-threaded version.

duckerude··on Nim vs Rust Benchmarks
That speedup is accomplished by using a third-party LRU implementation. I can also speed up the Rust version by 30% by plugging in https://lib.rs/crates/lru.

(The thread points out that the Nim version uses a doubly-linked list from the standard library while you'd need unsafe in Rust, and the lru crate does indeed use unsafe. So take that into account.)

duckerude··on An update on rust/coreutils
This thread inspired me—I found a way to remove 40K: https://github.com/clap-rs/clap/pull/3370

I'm sure it's possible to cut it more. It'll never be tiny, but it can be smaller than it is now.

duckerude··on An update on rust/coreutils
One more data point here is that uutils yes can be even faster than GNU yes by taking advantage of Linux's vmsplice syscall: https://github.com/uutils/coreutils/blob/main/src/uu/yes/src...

On my system it manages 20GB/s vs 6 GB/s (piping through pv into /dev/null).

duckerude··on An update on rust/coreutils
uutils already (optionally) builds that way. The GitHub release has a single 8.4M binary.

That doesn't seem so bad, the GNU coreutils Debian package has an Installed-Size of 17.9MB. Though there might be hidden drawbacks I don't know about.

duckerude··on An update on rust/coreutils
At least 500K comes from clap, the argument parsing library. That's how much true's size drops if you comment it out.

It's a lovely library. Generated help text with colors and line wrapping based on your terminal width, informative error messages that suggest corrections for typos, and a declarative model that lets you express subtle relationships between options. You don't get that from getopt.

But it's also pretty large. https://github.com/rust-cli/argparse-benchmarks-rs has some numbers.

(Disclaimer: I wrote one of the competitors on that page, lexopt. But I also use clap, depending on the project, and I'm happy with it.)

duckerude··on Annotated explanation of David Beazley's dataklasses
This looks like it would be pretty stable in practice. The iffiest assumption I see is that names are always ordered by first appearance.

It would be worse if it touched the bytecode itself, I've seen that break even in a bugfix release.

It actually works in PyPy (if you change the way dictionaries are merged so that it's 3.8-compatible). Other Python implementations could have a harder time.

A future Python release could always overhaul the bytecode system to the point this no longer makes sense. I think they'd consider it a backward compatibility break, but I'm not sure, and Python makes those fairly regularly regardless.

If the optimization were added to the standard library then it could just be kept in sync with the interpreter.

duckerude··on NGS – Next Generation Shell
If it works it works. Very few of my problems with shells can be blamed on C. (Maybe two in total?)

The oldest commit is from 2013. Rust hadn't hit 1.0 yet, Go only barely.

duckerude··on Rust maintainer perfectionism, or, the tragedy of Alacritty (2020)
I actually have experience with this specific code. I recently fixed a bug in a function that his PR also touches: https://github.com/nix-rust/nix/pull/1521

And I have a PR to fix problems in another Rust wrapper of the same libc functions: https://github.com/uutils/coreutils/pull/2653

It's hard to get right. Skimming the PR, the feedback looks reasonable.

duckerude··on The fish shell is amazing
> Posix shell is clunky has its limitations but there's nothing really flawed about it.

I think shellcheck is the best argument against this. POSIX shell is just stuffed to the brim with weird gotchas, places where the obvious way to write something causes subtle bugs down the line.

It's the only programming language I use that feels adversarial, where you have to be paranoid to properly solve simple tasks. (Admittedly some of that is because of the reliance on system programs rather than the shell language itself.)

duckerude··on How Python List Works
Python's semantics make homogeneity less useful and hard to enforce.

Everything's passed by reference, so the list has to contain pointers even if you know all its elements have the same type. And some objects can change their type by assigning to __class__, which can happen from a distance (because everything is by reference).

A careful implementation can solve this for some builtin types. PyPy does it for ints: if a list only contains ints it's represented as a proper numeric array. But then as soon as you append some other type the whole list is converted to a more expensive more traditional representation.

PyPy's ints still have to pretend to be heap-allocated objects at all times. In particular they have to do something tricky with the id() function, which on CPython just returns an object's memory address. The return value has to be unique and consistent for as long as an object lives.

duckerude··on Hell Is Other REPLs
> For example in Rust you can write a function that might mistakenly overflow a 32-bit integer and, in production it will just silently wrap. In WUFFS that function won't compile until you either decide explicitly what should happen (e.g. wrapping, saturation) for each overflow, or you trap all the cases where it could overflow as an error.

You can do this in Rust, up to a point. There's a lint to ban dangerous arithmetic: https://rust-lang.github.io/rust-clippy/master/index.html#in... . You can then use the {saturating,wrapping,checked,overflowing}_{add,div,mul,abs,...}() methods to decide exactly what should happen on overflow.

But WUFFS seems a lot nicer. Judging by the README it first tries to determine whether overflow is actually possible, while Clippy will happily forbid you from running "1 + 1".

duckerude··on ; Echo “Shell Injection”
I think it makes sense to prefer this hypothetical API:

  subprocess.run_escaped("tac -- %s | grep 1 > %s", "foo.txt", "bar.txt")
over this:

  with open("bar.txt", "w") as outfile:
      tac = subprocess.Popen(["tac", "--", "foo.txt"], stdout=subprocess.PIPE)
      grep = subprocess.Popen(["grep", "1"], stdin=tac.stdout, stdout=outfile)
  tac.wait()
  grep.wait()
(In practice the shell grammar is so grotesque that I'd never trust it to be 100% correct. But the idea is nice.)
duckerude··on Alphafold
> The AlphaFold parameters are made available for non-commercial use only, under the terms of the Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) license. You can find details at: https://creativecommons.org/licenses/by-nc/4.0/legalcode

Does CC BY-NC actually do this? As far as I can tell it only really talks about sharing/reproducing, not using.

Or is the only thing prohibiting other commercial use the words "available for non-commercial use only"?

duckerude··on Fully Homomorphic Encryption (FHE)
Couldn't regular asymmetric encryption take care of that? Just encrypt each entry with the patient's public key.

I think you only need homomorphic encryption if the transformation is something that the owner of data can't or shouldn't do themselves. It would be overkill for simple financial information.

duckerude··on AI Dungeon public disclosure vulnerability report
> don't include it in the model

They tried that, but they failed because they didn't know that it's possible to downcast from an interface.

It's really hard to have that kind of problem in a dumb REST API. `return {"name": record.name}` does what it says with hardly any magic. But if I write `return record` there's a whole extra layer that grabs information out of record, and I have to trust that it only grabs the information I want it to grab.

This is not to say that dumb REST APIs are definitely better. Having to do things manually also introduces risks.

← PreviousPage 2 of 12Next →