> Any large Rust project I check has tons of unsafe in its dependency tree.
This is an argument against encapsulation. All Rust code eventually executes `unsafe` code, because all Rust code eventually interacts with hardware/OS/C-libraries. This is true of all languages. `unsafe` is part of the point of Rust.
Having a 3D engine does not a AAA make. The Witness is a beautiful looking game, but the amount of state and interactions it has to deal with is orders of magnitude less than GTA: San Andreas. It is closer to the complexity a Myst remake would have.
This reads to me like an argument for better refactoring tools, not necessarily for looser type systems. Those tools could range from mass editing tools, IDEs changing signatures in definitions when changing the callers and vice versa, to compiler modes where the language rules are relaxed.
I was thinking about C++ and if you change your mind about whether some member function or parameter should be const, it can be quite the pain to manually refactor. And good refactoring tools can make this go away. Maybe they already have, I haven’t programmed C++ for several years.
If you want something more conservative for error reporting, annotate-snippets is finally at parity with rustc's current custom renderer and will soon become the default for both rustc and cargo.
It's actually a classic and much-repeated case in Rust education:
fn pick_first<'a>(x: &'a str, y: &'a str) -> &'a str {
x // We only actually return x, never y
}
fn main() {
let s1 = String::from("long-lived");
let result;
{
let s2 = String::from("short-lived");
result = pick_first(&s1, &s2);
} // s2 dropped here
println!("{}", result);
}
The error here is "borrowed value [pointing to &s2] does not live long enough". Of course it does live long enough, it's just that the constraints in the function signature don't say this usage is valid.
Thinking as a beginner, I think part of the problem here is the compiler is overstating its case. With experience, one learns to read this message as "borrowed value could not be proved to live as long as required by the function declaration", but that's not what it says! It asserts that the value in fact does not live long enough, which is clearly not true.
(Edit: having said this, I now realize the short version confuses beginners because of the definition of “enough”. They read it as “does not live long enough to be safe”, which the compiler is not—and cannot be—definitively saying.)
When this happens in a more complex situation (say, involving a deeper call tree and struct member lifetimes as well), you just get this same basic message, and finding the place where you've unnecessarily tied two lifetimes together can be a bit of a hunt.
My impression is that it's difficult or impossible for the compiler to "explain its reasoning" in a more complex case (I made an example at [0] [1]), which is understandable, but it does mean you always get this bare assertion "does not live long enough" and have to work through the tree of definitions yourself to find the bad constraint.
Would output along the following lines be an improvement?
error[E0597]: `buffer` does not live long enough
--> src/main.rs:63:21
|
59 | let report = {
| ------ borrow later stored here
60 | let mut buffer = Vec::new();
| ---------- binding `buffer` declared here
61 | let ctx = Context {
62 | config: &config,
| ------ this field and `buffer` are required by `Context` to have the same lifetime
63 | buffer: &mut buffer,
| ^^^^^^^^^^^ borrowed value does not live long enough
...
68 | };
| - `buffer` dropped here while still borrowed
help: consider making different fields in `Context` have independent lifetimes
|
4 | struct Context<'a> {
| ^^
5 | config: &'a Config,
| ^^
6 | buffer: &'a mut Vec<u8>,
| ^^
7 | }
It would be similar, pointing at the other argument. Neither case would have an applicable suggestion, just point in the right direction. I don't know if at the time we emit the error for `pick_first` we're able to evaluate the function's body to see if changing the lifetimes is feasible or not.
I feel like you're attacking a strawman here. Of course you can write unreliable software in Rust. I'm not aware of anyone who says you can't. The point is not that it's a magic talisman that makes your software good, the point is that it helps you to make your software good in ways other languages (in particular C/C++ which are the primary point of comparison for Rust) do not. That's all.
> The point is not that it's a magic talisman that makes your software good, the point is that it helps you to make your software good in ways other languages (in particular C/C++ which are the primary point of comparison for Rust) do not.
From their README, they promise better error messages, extensions when relevant (example: --progress), and Linux, macOS, Windows and other platforms support. It is my understanding that the GnuWin32 coreutils were last updated in 2017 and had some subtle differences to regular GNU coreutils, so that's one set of users for whom there's clear benefit. Faster speed could be accomplished if architectural changes are viable, but for many of these tools they are IO-bound.
> uutils coreutils aims to be a drop-in replacement for the GNU utils. Differences with GNU are treated as bugs.
> Our key objectives include:
> Matching GNU's output (stdout and error code) exactly
> Better error messages
> Providing comprehensive internationalization support (UTF-8)
> Improved performances
> Extensions when relevant (example: --progress)
> uutils aims to work on as many platforms as possible, to be able to use the same utils on Linux, macOS, Windows and other platforms. This ensures, for example, that scripts can be easily transferred between platforms.
Experimenting with better error messages, as test-bed for extensions that might not be able to be tried or accepted in GNU coreutils (for technical, social or other reasons), and being able to use the same tools in all major OS are very reasonable divergences from GNU's project to "justify" its existence.
The project was originally just a learning project for someone who wanted to learn Rust by reimplementing tools that were small, not a moving target and useful. From there, it grew as it found an audience of developers interested in productionalizing it. There have been coreutils ports for the languages you mention (go-coreutils, pycoreutils, coreutils-cpp, etc.), they just didn't (yet?) hit critical mass. It is a harder sell for GC-based projects in this case because they are unlikely to ever be included as part of a dirtribution's base. Lets not forget that coreutils themselves are a rewrite of previously existing tools to begin with.
As "unsafe". An example would be of how AMD GPUs some time ago didn't free a programs' last rendered buffers and you could see the literal last frame in its entirety. Fun stuff.
That is not a memory leak though! That's using/exposing an uninitialized buffer, which can happen even if you allocate and free your allocations correctly. Leaking the buffer would prevent the memory region from being allocated by another application, and would in fact prevent that from happening.
This is also something that Rust does protect against in safe code, by requiring initialization of all memory before use, or using MaybeUninit for buffers that aren't, where reading the buffer or asserting that it has been initialized is an unsafe operation.
It's a security hole. Rust doesn't prevent you from writing unsafe code that reads it. The bug wasn't that it could be read by a well conforming language, it was that it was handed off uninitialized to use space at all.
Measuring success of a project against a bar that the project didn't set is like complaining that an F1 car is hard to park: that's not what it was meant to do.
Servo was meant to be a test-bed for new architectures that might or might not be usable by Firefox. It was never meant to become Firefox' new web renderer, and it wasn't until more recently and long after the Mozilla-pocalypse that a new team took over the project with a goal of productionalizing the project as a whole. Stylo, for example, was integrated into Firefox 57 and allowed for parallel CSS style calculation, an effort that was tried unsuccessfully multiple times in C++.
This is an argument against encapsulation. All Rust code eventually executes `unsafe` code, because all Rust code eventually interacts with hardware/OS/C-libraries. This is true of all languages. `unsafe` is part of the point of Rust.
reply