HNHacker News
TopNewBestAskShowJobs

NobodyNada

3,649 karma · joined May 9, 2019

submissionscomments
NobodyNada··on Why Is SQLite Coded In C
> But (if I understand correctly) these are checks implicitly added by the compiler.

This is a dubious statement. In Rust, the array indexing operator arr[i] is syntactic sugar for calling the function arr.index(i), and the implementation of this function on the standard library's array types is documented to perform a bounds-check assertion and access the element.

So the checks aren't really implicitly added -- you explicitly called a function that performs a bounds check. If you want different behavior, you can call a different, slightly-less-ergonomic indexing function, such as `get` (which returns an Option, making your code responsible for handling the failure case) or `get_unchecked` (which requires an unsafe block and exhibits UB if the index is out of bounds, like C).

NobodyNada··on Why Is SQLite Coded In C
It's interesting to consider (and the whole page is very well-reasoned), but I don't think that the argument holds up to scrutiny. If such an automatic bounds-check fails, then the program would have exhibited undefined behavior without that branch -- and UB is strictly worse than an unreachable branch that does something well-specified like aborting.

A simple array access in C:

    arr[i] = 123;
...can be thought of as being equivalent to:

    if (i >= array_length) UB();
    else arr[i] = 123;
where the "UB" function can do literally anything. From the perspective of exhaustively testing and formally verifying software, I'd rather have the safe-language equivalent:

    if (i >= array_length) panic();
    else arr[i] = 123;
...because at least I can reason about what happens if the supposedly-unreachable condition occurs.

Dr. Hipp mentions that "Recoding SQLite in Go is unlikely since Go hates assert()", implying that SQLite makes use of assert statements to guard against unreachable conditions. Surely his testing infrastructure must have some way of exempting unreachable assert branches -- so why can't bounds checks (that do nothing but assert undefined behavior does not occur) be treated in the same way?

NobodyNada··on Traffic lights with four colors and a new white light are coming
> Follow the car ahead of me, if it’s autonomous.

The statement in the article is not conditional:

> follow the car in front of them (if that car is an autonomous one, it will know exactly when to continue or stop)

If the car in front of you is not autonomous, you're still supposed to follow it -- presumably, it's following an autonomous car further down the line. In other words, an autonomous car is responsible for stopping and starting human traffic behind it in the same lane, up to the next autonomous car behind it.

All the same, this sure does sound like it has a lot of failure modes.

NobodyNada··on Under the hood: Vec<T>
> if you `Vec::reserve_exact` with size N, fill to N, and then append a single element you are guaranteeing that that first append triggers a potentially O(N) resize.

The documentation does not guarantee this, because memory allocators can't typically allocate arbitrarily-sized blocks of memory, instead rounding to the nearest supported size. For example, for small allocations glibc malloc allocates in multiples of 8 bytes, with a minimum size of 24 bytes. So if you make a 35-byte allocation, there will be 5 bytes of wasted space at the end which you could theoretically use to store more elements without reallocating if your collection grows.

If you're using the system allocator, Rust can't take advantage of this, because the C malloc/free APIs don't provide any (portable) way for the allocator to inform the application about this excess capacity. But Rust's (currently unstable) API for custom allocators does expose this information, and the documentation is written to allow Vec to take advantage of this information if available. If you reserve_exact space for 35 u8's, and your allocator rounds to 40 bytes (and informs the Vec of this the allocator API), then the vector is allowed to set its capacity to 40, meaning the next append would not trigger a resize.

On current stable Rust, this is all just theoretical and Vec works as you describe -- but the documentation specifically does not promise this because the situation is expected to change in the future.

NobodyNada··on Under the hood: Vec<T>
In this case, the implementation is not free to ignore calls to reserve or reserve_exact -- the documentation makes explicit guarantees about this: "After calling reserve, capacity will be greater than or equal to self.len() + additional".

This matters for performance reasons, and also because unsafe code is allowed to use the extra capacity of the vector -- e.g. you can write to the unused space and then call set_len to insert elements in-place. If `reserve` did not guarantee the requested capacity was allocated, this would cause buffer overflows.

The documentation for these functions is very carefully worded to explain exactly what each method does and does not guarantee. Both functions have the exact same hard guarantees: after calling the function, the vector will have at least enough capacity for the additional elements.

The difference is in what the methods try to do without guaranteeing exact behavior: reserve follows the usual amortized growth pattern, while reserve_exact tries to avoid overallocating. These are described as best-effort performance optimizations rather than guarantees you can rely on because 1) the amortized growth strategy is subject to change between platforms and compiler versions, and 2) memory allocators typically don't support arbitrarily-sized blocks of memory, and instead round up to the nearest supported size.

NobodyNada··on German government comes out against Chat Control
> I strongly oppose any kind of "panopticon" like ChatControl. What I would like to see, is, say, Signal [...] provide cleartext contents of communications to/from a cleary identified party

Those statements simply aren't compatible.

Right now, Signal is designed by cryptography experts to provide the best privacy we know how to build: messages are only readable by you or the intended recipient. "Lawful intercept" necessarily means some additional third party is given the ability to read messages.

It doesn't matter what kind of legal framework you have around that, because you can't just build a cryptosystem where the key is "a warrant issued under due process." There has to be a system, somewhere, that has access to plaintext messages and can give law enforcement and courts access. The judges, officers, technicians, suppliers, and software involved in building and using this system are all potential vectors by which this access can be compromised or misused -- whether via software or hardware attacks, social engineering, or abuse of power.

Maybe your country has "functioning rule of law", and every single government official and all the vendors they hire are pure as snow, but what about all the rest of us living in imperfect countries? What about when a less-than-totally-law-abiding regime comes into power?

You're proposing that we secure our private conversations with TSA luggage locks.

NobodyNada··on DARPA project for automated translation from C to Rust (2024)
Right. The comment you replied to said "Rc/Arc and some manual management" -- "some manual management" implying that you need to account for the change to semantics in the manner appropriate for your application.
NobodyNada··on DARPA project for automated translation from C to Rust (2024)
The discussion is about a project for securing legacy code by machine-translating it to Rust. Fil-C is an alternative way to secure legacy code using a different C implementation. I think that's highly relevant to the discussion.
NobodyNada··on DARPA project for automated translation from C to Rust (2024)
> The argument for using Rust instead of a memory safe implementation of C is all about performance. And that’s not a bad argument! But we should be honest about it.

This is half of it; the other half is that there's more to writing correct code than just memory safety.

Rust was not initially designed as a memory-safe "C alternative" -- it started as a higher level language with green threading and garbage collection. Borrow checking, affine types, and all that were introduced not to "solve" memory safety, but to reduce logic bugs in complex, highly concurrent code by enabling programmers to statically check application invariants using the type system. Once it became apparent that this system was powerful enough to express the semantics of full memory safety without garbage collection, GC was removed from the core of the language, and Rust became what it looks like now.

The point is, I think focusing on "memory safety" seriously undersells Rust. Rust is aimed at giving you tools to statically verify arbitrary correctness properties of your program; and memory safety is just one example of how the standard library uses the available tooling to prove one particularly useful property. But Rust's "safety" protects you from any logic bug that you define to be a safety issue.

Additionally, this means that you're not restricted to one particular runtime or environment's ideas of memory safety -- you can write or bring in unsafe code to define your own requirements. This is very important to me -- I work on low-level driver and RTOS code for microcontrollers, and am commonly doing very unsafe things with task switching, interrupts, DMA, memory layout and pointer casting tricks, inline assembly, hardware MMIO, power management, etc. Rust fits fantastically in this niche, because I can use the type system to write out all the preconditions that external users must uphold when interacting with my driver code.

I think Fil-C is an excellent project, and is far more practical and realistic than TRACTOR for the problem of securing legacy code. But I also see it as largely orthogonal to Rust -- I can't see many situations where someone would choose Fil-C for greenfield code over something like Java or Go. Rust has safety, performance, and interop advantages over GC'd languages; and safety advantages over other C-like languages; so it's a great choice for new projects, but it's not gonna help you with all your old code like Fil-C will.

> It’s also annoying that this DARPA page mentions some kind of consensus. There’s no consensus on Rust being the safe bet.

The "consensus" the article talks about is not Rust, it's that "relying on bug-finding tools is not enough." Swift, C++-with-[the right]-changes, and Fil-C all would fall within that consensus (of needing some sort of guaranteed safety); only Zig is something of an exception in your list.

NobodyNada··on DARPA project for automated translation from C to Rust (2024)
Sure it does. Make the backreferences weak, or use a library that provides cycle detection (or even full-on tracing GC if you really want).
NobodyNada··on Kagi News
I'm about as AI-pessimist as it gets, but Kagi's use of LLMs is the most tasteful and practical I've seen. It's always completely opt-in (e.g. "append a ? to your search query if you want an AI summary", as opposed to Google's "append a swear word to your search query if you don't want one"), it's not pushy, and it's focused on summarizing and aggregating content rather than trying to make it up.
NobodyNada··on Noise cancelling a fan
> Probably somewhere on the internet is a fantastic interactive diagram that would clearly demonstrate this for you, but I couldn't google one up. Links solicited.

Here's one: https://apenwarr.ca/beamlab -- as well as the author's writeup: https://apenwarr.ca/log/20140801

The author is focused on beamforming WiFi signals, but the principle is exactly the same whether it's a radio wave or a sound wave.

NobodyNada··on Doorbell prankster that tormented residents of apartments turns out to be a slug
For what it's worth, your initial comment showed for me as dead (i.e. shadowbanned) until I vouched for it (within an hour or so after you posted it). It would appear you managed to trip some filters with this thread :)
NobodyNada··on China bans one-pedal driving in default modes by 2027
> it's banned some places

"Engine braking" bans usually target jake braking [0] (opening the exhaust valve at the end of the compression stroke), which greatly increases the effect of engine braking at the cost of producing a very loud noise [1] (which is why it's often banned in residential areas). As far as I'm aware, such bans do not limit the use of "normal" downshifting to decelerate.

[0]: https://en.wikipedia.org/wiki/Compression_release_engine_bra...

[1]: https://youtu.be/z3bLqjPBlx8

NobodyNada··on Axial twist theory
This is definitely an actual phenomenon on HN -- someone posts something interesting and it gets a bunch of HNers to go down the rabbit hole together.

A couple weeks ago there was one about leeches:

"You shouldn't salt a leech that's sucking your blood" https://news.ycombinator.com/item?id=45001005

"How Leeches Made Their Comeback" https://news.ycombinator.com/item?id=45045235

"Leeches and the Legitimacy of Folk-Medicine" https://news.ycombinator.com/item?id=45009629

"This is my brain on leeches" https://news.ycombinator.com/item?id=45037109

NobodyNada··on Apple expects to notify 100M people that they have hypertension in a year
I think the headline is wrong: at 20:18 in Apple's presentation, the presenter said they expect to notify one million people during the year, not 100 million.
NobodyNada··on Hollow Knight: Silksong causes server chaos on Xbox, Steam, and Nintendo
Of course. I usually don't 100% metroidvanias for this reason. But I also don't usually look up a guide (unless I really want to find a specific boss or secret or something). I just put the game down and come back to it later if I want to keep exploring it.

There's nothing wrong with using a guide if that's how you have the most fun. But I think most people would have more fun playing Hollow Knight without one, which is why I don't think "if you [play Hollow Knight], use a guide or something" is good advice as an unqualified statement. I enjoy the experience of playing a game far more than the accomplishment of having completed it, so I'd rather enjoy a game fully and leave it unfinished than halfheartedly rush through it with a guide.

NobodyNada··on Hollow Knight: Silksong causes server chaos on Xbox, Steam, and Nintendo
#2 seems like a technically solvable problem: AES-encrypt the preload and distribute the key on launch day.
NobodyNada··on Hollow Knight: Silksong causes server chaos on Xbox, Steam, and Nintendo
Sure, but the viral meme aspect is because HK was such a well received game and Silksong has been so highly anticipated for so long, not the other way around. It certainly contributes to the hype today, but memes are not the reason for the initial success of Hollow Knight and the anticipation of Silksong. The memes really took off around 2021-2022 after Silksong had been in development for several years with no release in sight and people started wondering how long of a wait they were in for.
NobodyNada··on Hollow Knight: Silksong causes server chaos on Xbox, Steam, and Nintendo
I don't think that's necessary. Hollow Knight is a game whose appeal comes from exploring a massive, beautiful world and and discovering its secrets -- using a guide would take away a lot of the fun, at least for me.

Yeah, HK is a huge game and Silksong is probably bigger; it'll take a while to play through both. But that's a good thing. Play these games for the experience of playing them, no need to rush through them just to check them off a list.

NobodyNada··on The time picker on the iPhone's alarm app isn't circular, it's just a long list
Applying this to answer the question directly -- no, this doesn't waste CPU cycles or memory because UIPickerView only keeps the visible rows in memory and generates them lazily as you scroll. Thus, the number of rows does not affect the performance of the picker.

All table- or list-like UI components across Apple's platforms work this way.

NobodyNada··on Unexpected productivity boost of Rust
Zig's trying to be a "nicer C" that's easy to learn and fast to compile. It's a great language with a lot of neat design, and definitely setting itself up to be a "valid challenger" in a lot of the systems-y, performance-focused domains Rust targets. But it's not trying to compete with Rust on the safety/program correctness front.

Almost none of the Rust features discussed in this subthread are present in Zig, such as ownership, borrowing, shared vs. exclusive access, lifetimes, traits, RAII, or statically checked thread safety.

NobodyNada··on Unexpected productivity boost of Rust
I was specifically responding to the statement "Zig and rust do not have the same scope. I honestly do not think they should be compared".

I don't know enough Zig to have a qualified opinion on the particular example (besides being very surprised it compiled). However, I thought this post from the front page the other day had more practical and thoughtful examples of this kind of thing: https://www.openmymind.net/Im-Too-Dumb-For-Zigs-New-IO-Inter...

NobodyNada··on Unexpected productivity boost of Rust
Rust and Zig are the two most prominent new systems programming languages. It's only natural for people to compare them, from perspectives such as "I'm starting a project and need to choose what language to use".

The languages are very different in scope, scale, and design goals; yes. That means there's tradeoffs that might make one language or the other more suitable for a particular person or project, and that means it can be interesting and worthwhile to talk about those tradeoffs.

In particular, Rust's top priority is program correctness -- the language tries hard not to let you write "you're holding it wrong" bugs, whereas Zig tends to choose simplicity and explicitness instead. That difference in design goals is the whole point of the article, not a reason to dismiss it.

NobodyNada··on Unexpected productivity boost of Rust
The way that this fully works together is:

- The return type of Mutex::lock() is a MutexGuard, which is a smart pointer type that 1) implements Deref so it can be dereferenced to access the underlying data, 2) implements Drop to unlock the mutex when the guard goes out of scope, and 3) implements !Send so the compiler knows it is unsafe to send between threads: https://doc.rust-lang.org/std/sync/struct.MutexGuard.html

- Rust's implementation of async/await works by transforming an async function into a state machine object implementing the Future trait. The compiler generates an enum that stores the current state of the state machine and all the local variables that need to live across yield points, with a poll function that (synchronously) advances the coroutine to the next yield point: https://doc.rust-lang.org/std/future/trait.Future.html

- In Rust, a composite type like a struct or enum automatically implements Send if all of its members implement Send.

- An async runtime that can move tasks between threads requires task futures to implement Send.

So, in the example here: because the author held a lock across an await point, the compiler must store the MutexGuard smart pointer as a field of the Future state machine object. Since MutexGuard is !Send, the future also is !Send, which means it cannot be used with an async runtime that moves tasks between threads.

If the author releases the lock (i.e. drops the lock guard) before awaiting, then the guard does not live across yield points and thus does not need to be persisted as part of the state machine object -- it will be created and destroyed entirely within the span of one call to Future::poll(). Thus, the future object can be Send, meaning the task can be migrated between threads.

NobodyNada··on Starship's Tenth Flight Test
...Huh? Why would that be sad?

My parents have formative memories of watching the moon landings as kids, and I of watching the space shuttle. As someone who ended up where I am in large part due to my curiosity about the world growing up, reading of OP's kid watching a rocket launch, thinking critically about what they're seeing, and learning more about the world from it is a joyous thing.

NobodyNada··on Ask HN: Why hasn't x86 caught up with Apple M series?
> Apple CPU's are even optimized for Apple software GC calls like Retain/Release objects.

I assume this is referring to the tweet from the launch of the M1 showing off that retaining and releasing an NSObject is like 3x faster. That's more of a general case of the ARM ISA being a better fit for modern software than x86, not some specific optimization for Apple's software.

x86 was designed long before desktops had multi-core processors and out-of-order execution, so for backwards compatibility reasons the architecture severely restricts how the processor is allowed to reorder memory operations. ARM was designed later, and requires software to explicitly request synchronization of memory operations where it's needed, which is much more performant and a closer match for the expectations of modern software, particularly post-C/C++11 (which have a weak memory model at the language level).

Reference counting operations are simple atomic increments and decrements, and when your software uses these operations heavily (like Apple's does), it can benefit significantly from running on hardware with a weak memory model.

NobodyNada··on Optimizing our way through Metroid
There have definitely been some applications of this sort of thing to speedrunning -- though far less sophisticated than the approach here, and usually only testing against a very small subset of the game. I've heard of some of this kind of work being done before on e.g. SM64.

I've also done something along these lines myself in Super Metroid. Mother Brain's neck moves in a conceptually simple but very chaotic pattern influenced by Samus's vertical movement, and there's a cutscene during the fight where the positioning of her neck can make a difference of about 7 seconds. The TAS fight used complicated movement to manipulate her neck position developed through much trial-and-error, while the best known human-viable manips were several seconds slower.

I wrote a program to search the state space for optimal movement patterns, and working with some speedrunners we were able to come up with a new human-viable manipulation that matched the previous TAS fight, as well as a new TAS manipulation that saved an additional 41 frames.

https://youtu.be/7SHD9L_Jx5Q

https://github.com/NobodyNada/mbsim

NobodyNada··on It’s not wrong that "\u{1F926}\u{1F3FC}\u200D\u2642\uFE0F".length == 7 (2019)
That works! (The braces are droppable for 16-bit codepoints in JS, but required in Rust.)
NobodyNada··on It’s not wrong that "\u{1F926}\u{1F3FC}\u200D\u2642\uFE0F".length == 7 (2019)
That would be "\U0001F926\U0001F3FC\u200D\u2642\uFE0F" in Python's syntax, or "\u{1F926}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}" in Rust or JavaScript.

Might be a little long for a title :)

← PreviousPage 4 of 22Next →