HNHacker News
TopNewBestAskShowJobs

dureuill

456 karma · joined July 24, 2021

submissionscomments
dureuill··on White House urges devs to switch to memory-safe programming languages
`let` defines a new binding, as opposed to changing the value of an existing binding. You can't remove `let` to keep only `=` because it has a different use case.

Not indicating the type is idiomatic in Rust, but you can annotate it:

    let commit_message: String = repo.head().peel_to_commit().ok()?.message()?.into();
Here this is useful to specify the type `into` should convert to. However, if rewritten as:

    let commit_message = repo.head().peel_to_commit().ok()?.message()?.to_string();
Then it is useless because we're already specifying the type of the variable by using `to_string`.

Note that IDEs are displaying type hints anyway (without you having to type them), so you don't have to suffer if walking through a codebase where people are annotating their types too little for comfort

dureuill··on Too dangerous for C++
Interesting, I was lacking this context. Could you provide me with more information about this? I only saw atomic_shared_ptr come up in discussions about bugs up to now.
dureuill··on Too dangerous for C++
I would rather use the non-atomic shared pointer from Boost[1] linked upthread than a non-standard non-portable implementation detail from GCC, but yes, it exists.

You can definitely implement a non-atomic non-threadsafe shared pointer in C++, my point in the article is that actually using it is very error prone. This is supported by the type being excluded from the standard library with one of the reasons being the risk of bugs.

[1]: https://www.boost.org/doc/libs/1_65_0/libs/smart_ptr/doc/htm...

dureuill··on Too dangerous for C++
The point of the article isn't that single-threaded non-atomic shared pointers are unfeasible in C++, it is that their usage is too dangerous.

The fact that it wasn't included in the standard library for this reason is an argument for this. The fact that even `shared_ptr` has thread-safety footguns, one of which made it to the famous C++ talk, “Curiously Recurring C++ Bugs at Facebook”[1], is another. By the way, every single of the bugs from that talk is impossible in safe Rust.

[1]: https://youtu.be/lkgszkPnV8g?si=cCWASihvIGJ25Jf3

dureuill··on Too dangerous for C++
Yes, this is discussed in the article, I do not understand your point.
dureuill··on Too dangerous for C++
In this context, "unsynchronized access" refers to read/write operations happening concurrently on multiple threads, *not* to the shared pointers pointing to different objects as a result of the assignment.

Unsynchronized access to the pointed to object will typically cause a specific kind of race condition called a data race, which is undefined behaviour. As it requires threads, it cannot happen in a single-threaded context.

dureuill··on Too dangerous for C++
> which is by definition a non-thread-safe operation

yes, but at this point, since the reference count is reaching 0, there is supposed to be only that one thread accessing the object being destroyed, so the destruction not being thread-safe should not be a problem.

If otherwise, it means there was a prior memory error where a reference to the pointed-to object escaped the shared_ptr. From there the code is busted anyway. By the way it cannot happen in Rust.

> Different runs result different threads calling the destructor

What adverse effects can happen there? I can think of performance impact, if a busy thread terminates the object, or if there is a pattern of always offloading termination to the same thread (or both of these situations happening at once). I can think of potential deadlocks, if a thread holding a lock must take the same lock to destroy the object (unlikely to happen in Rust where the Arc object would typically contain the object wrapped in its mutex and the mutex wouldn't be reused for locking other parts of the code). There isn't much else I can think of, what do you have in mind?

> whether a lambda is currently holding a reference to the object, or its members

This cannot happen in Rust. If a lambda is holding a reference to the object, then it either has (a clone of) the Arc, or is a scoped lambda to a borrow of an Arc.

dureuill··on Too dangerous for C++
"performance" is about profiling and avoiding bottlenecks.

That I'm using reference counting on the error path of my parser (so there's something wrong with the input and the task is not going to complete) is very unlikely to become a bottleneck.

You may have a point in that I navigated codebases that were plagued with shared ptrs everywhere in the past, and that general style of programming is not going to yield good performance. But you shouldn't deal in absolutes.

dureuill··on Too dangerous for C++
This strikes me as a weird criticism.

If I hired someone to paint my wall and they were saying "I'm not going to use any protection against splatters on the ground because I am that good and don't need training wheels", I would find that behavior very unprofessional and wouldn't want that person anywhere close to my wall.

I'm writing professional software I'll take any help from tooling that is available without compromising other aspects like performance. It also helps that Rust is much more productive than C++ overall.

About the self-own, my teams over the years lauded my low bug rate, be it in C++ or in Rust. I have a knack for correctness, hence why I prefer languages that make strong guarantees about it by construction to languages where I need to remember and regurgitate thousands of rules at every corner

dureuill··on Improving Interoperability Between Rust and C++
The issue with this approach lies with the current content of the stdlib: the stdlib is for vocabulary types, that is, types that are meant to be used as interoperability bricks in almost all programs (think Option, Result, Vec).

If you start versioning the stdlib now you need a bridge between the Option from std1.0 and the Option from std2.0. This will become confusing quickly for programs that use libraries that depend on different versions of the std.

We could forbid that situation, but then we have an ecosystem split

dureuill··on Out-of-bounds read and write in the glibc's qsort()
It is both.

"Memory safe" means that if the user is "holding it wrong", it won't cause a memory error.

In safe Rust, it is impossible to call free with the wrong address (simply by virtue of the `free` equivalent being unsafe, and with the pervasive use of smart pointers to make it practical to stay in safe land).

Similarly even if you "hold it wrong" and use a wrong comparison function in Rust's sort, you might get nonsensical results, but no memory errors.

That's precisely what safety means in this context

dureuill··on OpenD, a D language fork that is open to your contributions
You make it sound like Rust made these choices for the heck of it, whereas there are very good reasons why impl blocks are separate from the struct:

1. Make class methods open to extension. This allows adding methods from other contexts, including other privacy contexts. Sure you could have dedicated syntax like C#'s extension methods, but these were added after the fact. If starting your language from scratch, while have two distinct syntaxes, when one will do?

2. Allows to add methods that only exist for some combinations of generic parameters. For example you can have a generic `Foo<T>` class, and then define a method `from_bar` that only exists for `Foo<Bar>`. Again, impl blocks may not be the only solution to this, but it is a highly cohesive one.

3. Lastly this is more philosophical, but it decouples the data definition from the method definition.

Not wanting to try Rust because its syntax is unfamiliar has to be the weakest reason, especially when the syntax has excellent reasons to be different.

dureuill··on Zig cookbook: collection of simple Zig programs that demonstrate good practices
> That link is about learning rust.

While this is true for most of the article, I think the quoted sentence is about more general Rust use.

> In my experience, this is not a big effort at writing new code, but at refactoring/maintenance

In my experience working on the Meilisearch codebase, Rust is the easiest language to refactor, because it catches so many errors at compile time:

1. Most languages lack the `mut` binding modifier that allows to warn when the binding does not need to be mutable (catching errors when part of the code hasn't been updated)

2. Most languages lack exhaustive initialization, destructuring and matching, that catch pretty much all the situations where a new field has been added to a struct.

3. Let's not speak about languages with null, or without static typing

Having more type information (including lifetimes) makes refactorings easier, not harder. I concede it causes a bit more boilerplate but that's not the bottleneck when writing code.

dureuill··on Zig cookbook: collection of simple Zig programs that demonstrate good practices
[citation needed].

Here's a reference to the contrary[1]:

> Overall, we’ve seen no data to indicate that there is any productivity penalty for Rust relative to any other language these developers previously used at Google.

[1]: https://opensource.googleblog.com/2023/06/rust-fact-vs-ficti...

dureuill··on Zig cookbook: collection of simple Zig programs that demonstrate good practices
Because gov agencies say so?

Also, memory safety vulnerabilities is the largest class of vulnerability by count today by a large margin (70% of the total) and cost a lot of money to bigger companies.

As changing language stacks also cost a lot of money, there needs to be a strong incentive to do so. Getting memory safety without compromising applicability to the lowest levels of the stack is one such incentive.

Rust also doesn't have a lot of drawbacks, from my experience (professional use for years, coming from C++) and from recent reports from Google[1] (in particular, the learning curve steepness[2] and productivity penalty[3] are overstated).

[1]: https://opensource.googleblog.com/2023/06/rust-fact-vs-ficti...

[2]: Anecdotally, these ramp-up numbers are in line with the time we’ve seen for developers to adopt other languages, both inside and outside of Google. (Same report)

[3]: Overall, we’ve seen no data to indicate that there is any productivity penalty for Rust relative to any other language these developers previously used at Google. (Same report)

dureuill··on Zig cookbook: collection of simple Zig programs that demonstrate good practices
> Is make considered bad or something?

Depends what you're using it for. Generated from CMake, Makefiles are OK I guess, although Ninja is strictly better these days.

Manually written, I would say make is bad.

1. It doesn't promote portability: since each target calls shell commands, it is very easy to accidentally create a non portable Makefile (something alleviated when using CMake that handles the portability before emitting the Makefile)

2. It is imperative and stateful in subtle ways, which is the wrong level of abstraction for a build system. Systems that are declarative and describe the desired state of the build are much easier to use and reliable

3. It is bare bone as far as build system functionality goes: it doesn't provide many of the useful things I expect from a modern build system: the concepts of library dependencies, build profiles, compiler flags, test, documentation or even project in general... are all orthogonal to make

4. It is ad-hoc: as a result of the previous points, the expected functionality of a build system is often re-created in the makefile, but in a non fully standard way (conventions exist, but must be manually enforced and are limited). This results in brittle and difficult to use build systems.

> What is an alternative

Language-specific package managers, notably Cargo.

If using rust, there's also a pattern called xtask[1] that's aiming at providing a standard interface in the form of a binary (since it is rust, it is portable, but most of the other points unfortunately apply).

Otherwise, CMake, meson, nix, bazel, buck2

[1]: https://github.com/matklad/cargo-xtask

dureuill··on Rust 1.75.0
We have to be careful when using impl trait in return position in public APIs.

The opaqueness means that the consumer of the API cannot name the type and cannot store it inside of a struct (without making it generic (moving the issue one level higher), or boxing).

I will consider this feature complete once we get the ability to name these opaque types, via a type alias impl trait.

dureuill··on Meilisearch expands search power with Arroy's filtered disk ANN
AFAIK, not yet. It was a goal of Mimir, but LMDB (used by arroy on particular and Meilisearch in general) uses filesystem and os features (mmap, file locking) that are incompatible with wasm.

If you're interested in bringing WASM support, I think the most promising avenue was to support redb as a heed backend. Heed is the high level binding library we're using to talk to LMDB. If you're interested in doing this, you can get in touch with the maintainer of mimir.

As a Meilisearch team member, I'd love to participate as well, but I'm afraid I won't have the bandwidth

dureuill··on Meilisearch expands search power with Arroy's filtered disk ANN
One of our top oss contributors is developing mimir as an embedded Meilisearch: https://github.com/GregoryConrad/mimir/tree/main/packages/mi...
dureuill··on Rust to stabilize `async fn` and return-position `impl Trait` in traits
It increases the places it can be used, but it decreases what type can be returned. Not all types can be sent between threads (in particular, types that feature unsynchronized mutability through shared references are not Send). If you executor is local to one thread, it can be a blocker if a trait you use suddenly requires your future to be Send.
dureuill··on Trying out C++20's modules with Clang and Make
why would be using a "fancy tool" a bad thing? Headers are to be maintained manually, which is lost productivity and invite errors. The "fancy tools" can provide specialized search, formatting of the documentation, internal links, formatted and tested code examples, and of course the interface for free. In modern languages they're even part of the included tools.

I'm having a hard time seeing the downside of the "fancy tools".

dureuill··on Trying out C++20's modules with Clang and Make
I agree that the compact API overview is a good thing to have, but I loathe that C forces the user to write it by hand, while modern languages just generate it from the documentation comments of the source.

See: `cargo doc` that generates nice HTML documentation. See also language servers that provide completion using the same documentation comments.

Meanwhile headers, on top of the forced duplication of signatures between header and implementation, and the associated cost, is also implemented on terms of the preprocessor and textual inclusion. This compilation model makes code analysis much harder than necessary, because it becomes hard to know which piece of the preprocessed compile unit comes from which header.

This makes it hard to implement e.g. "remove unnecessary import" or "import symbol automatically" functionalities that are the bread and butter of module-based languages. Also impacts automatic refactoring negatively

dureuill··on Trying out C++20's modules with Clang and Make
> Modules adoption would be severely stunted if everyone had to adjust all calling code in order to convert a library to use modules.

"We'd like this new feature to be good, but we have to cripple it for adoption/back-compatibility reasons". This is the entire story of C++, sadly :(

Modules being orthogonal to namespaces means:

1. Bad Surprise for people first using modules, leading to incomprehension as this is different from every other language

2. Occasional Bad Surprises for people using modules even after the first time, as people will export stuff outside of namespaces/in the wrong namespace from time to time

3. Some code using the convention that the namespaces in modules follow the module "hierarchy" (which is also entirely a conventional concept because `.` is not treated as a special character) and some code not following that convention, either because it was hastily ported to modules or because the authors elected not to care for this particular convention. Now enjoy not knowing that when importing a module from a third-party library.

Unfortunately the orthogonality to namespaces, in the name of "facilitating migration", cripples the language forever and actually reduces the motivation for migrating to modules (because it doesn't remove footguns). Doesn't help that the whole module feature is designed in this spirit. I'm lucky enough to have migrated to greener languages, but I don't think I would find it worth it to migrate to modules if I were still in the position of deciding what C++ features to introduce in my team.

> Ideally one could find/replace includes with analogous imports, a bit at a time.

Make a tool like `cargo --fix`, or something. Ah no, we can't, because of headers (and more generally the preprocessor), ironically :(

dureuill··on Trying out C++20's modules with Clang and Make
Of course you're correct that Rust has no support for making the `Self` type itself generic, which is the core of the "deducing this" feature.

However, "deducing this" has the side-effect of allowing to explicitly specify the "this" parameter in the argument list. The syntax looks the same as Rust, and the convention of calling this parameter "self" is taken from "other languages" (Python, Rust, ...)[0].

Similarly, it could be argued that the ability to pass the object by value in method is lifted from Rust. That's how I understand the GP comment anyway.

[0]: https://devblogs.microsoft.com/cppblog/cpp23-deducing-this/ "You don’t have to use the names Self and self, but I think they’re the clearest options, and this follows what several other programming languages do."

dureuill··on Meta wants to charge EU users $14/month if they don't agree to personalized ads
If that interpretation is correct, how can websites like lemonde.fr propose to either accept all cookies or get a paid subscription, under the GDPR?

Is that actually illegal? Or am I missing a difference with what you're describing?

dureuill··on How to Use Monadic Operations for `std:optional` in C++23
I wish we had the option to specify explicit captures in rust closures too. When you need it you need it, and it is good for clarity. The current "add `move` to switch from by-reference to by-value" is too coarse grained
dureuill··on Google assigns a CVE for libwebp and gives it a 10.0 score
The most recent high severity vulnerability I could find in libjpeg is CVE-2016-6702[0].

If you're willing to count libjpeg-turbo, there's also CVE-2020-17541[1].

For h.264, if you're willing to count Firefox or gstreamer, we're talking CVE-2022-3266 or CVE-2021-3185 (the latter is even critical)

[0]: https://nvd.nist.gov/vuln/detail/CVE-2016-6702

[1]: https://nvd.nist.gov/vuln/detail/CVE-2020-17541

dureuill··on The urgent need for memory safety in software products
I really don't see what issues there are with rust that could be addressed by another language without compromise on the performance and memory safety axes.

I can think of two reasons for people not moving: 1. inertia, aka "not going out of comfort zone to learn new things" ("learning" includes understanding why things are different, not merely knowing they are). This won't be addressed by another new language that people will also need to learn.

2. Existing codebases won't magically turn from C++ to Rust. Legacy is actually a good reason to still be using C++ today, but another language also won't address this, as the fundamental issue is you can't change the existing code to be safer without effort.

In both cases, time and effort on the language best positioned to replace C++ (Rust) will alleviate these issues. In particular this effort could be spent to fully close the feature gap with C++ (specialization, more expressive consts, ...) and improving Rust/C++ interoperability.

No need to "shame" people, but finding a way to replace C++ use by Rust use is how we will solve the problem.

dureuill··on Patterns with Rust Types
I think I'd implement Serialize on a different type and implement conversions from the foreign type to my serializable type.

That would mean that the locations where I want to serialize the foreign type would have to be marked with `.into()`, but that would be the sole maintenance overhead.

dureuill··on C++ Papercuts
> Backwards from what? A reference can't be null, but it can still be invalid, which is a more common problem than forgetting to check for null-ness.

no, a reference cannot be invalid. If you are given an invalid reference (such as referring to an object after its lifetime ended, or referring to something that is not an object of the reference's type), then the fault lies on the caller. The callee cannot check the reference's validity, nor it is its responsibility to do so.

This is *very* different from the case of a pointer, where the caller is not at fault, typesystem wise, for giving a nullptr to a function accepting a pointer.

Increasing the probability of runtime errors to gain a little bit of readability at the caller's site is not a good tradeoff.

← PreviousPage 2 of 7Next →