> Ability to temporarily borrow exclusively owned objects as either shared or exclusive-mutable adds enough flexibility.
Rust quietly has several other features in order to improve the quality-of-life of its ownership model. Two examples: if you have a mutable reference then Rust will coerce it to an immutable reference if one is required, and if you have a mutable reference then Rust will transparently re-borrow it when calling functions that accept mutable references in order to allow you to use the mutable reference more than once despite the fact that they do not implement Copy.
> I do wish not handling a Result was a hard error, and not just a warning via `must_use`
It is possible to turn any Rust warning into an error with the use of the `deny` attribute. In addition it is now possible to configure the behavior of lints in the `lints` section of Cargo.toml.
I have been aware of this proposed initiative for some time and I find it interesting that it is now becoming public. It is a very ambitious proposal and I agree that this level of ambition is appropriate for DARPA's mission and I wish them well.
As a Rust advocate in this domain I have attempted to temper the expectations of those driving this proposal with due respect to the feasibility of automatic translation from C to Rust. The fundamental obstacle that I foresee remains that C source code contains less information than Rust source code. In order to translate C code to Rust code that missing information must be produced by someone or something. It is easy to prove that it is impossible to infallibly generate this missing information for the same reason that scaling an image to make it larger cannot infallibly produce bits of information that were not captured by the original image. Instead we must extrapolate (invent) the missing information from the existing source code. To extrapolate correctly we must exercise judgement and this is a fallible process especially when exercised in large quantities by unsupervised language models. I have proposed solutions that I believe would go some way towards addressing these problems but I will decline to go into detail.
Ultimately I will say that I believe that it is possible for this project to achieve a measure of success, although it must be undertaken with caution and with measured expectations. At the same time it should be emphasized it is also possible that no public result will come of this project and so I caution those here against reading too much into this at this time. In particular I would remind everyone that the government is not a singular entity and so I would not interpret this project as a blanket denouncement against C or vice versa as a blanket blessing of Rust. Each agency will set its own direction and timelines for the adoption of memory-safe technologies. For example NIST recommends Rust as well as Ada SPARK in addition to various hardened dialects of C/C++.
> In order to translate C code to Rust code that missing information must be produced by someone or something.
If you don't go for preserving the formal semantics of C code and instead only require the test-suite to still pass after translation that can provide a lot of wiggle room for the translation. This is how oxidation projects often work in practice.
Fuzzers can also help with generating additional test data to get good branch coverage.
> Most serious programs will use write() and then check the return value.
Similarly in Rust, serious programs will use writeln rather than println, and will receive the standard compiler warning if the Result produced by writeln is ignored.
Let us be clear that the notion of "change" being referred to here is forward compatibility, not backward compatibility. The user is commenting on the fact that Rust library authors make use of new features as they become available, and as a result in order to compile Rust code you will often need a recent version of the compiler, or otherwise you will need to find an older version of the library in question.
In addition Rust was born from Mozilla and imitates Firefox's rapid release schedule of one release per six weeks. This does not mean that Rust releases are substantial or that they are traumatizing, only that they are frequent. The contract with users is that Rust releases must be painless so as to not fatigue users and discourage them from upgrading. The success of this painless upgrade strategy is proved by the fact that library authors are so quick to upgrade, as mentioned.
This is in contrast to other languages where historically a new version of their compiler might only be released as infrequently as once per three years. It seems that these languages have begun taking queues from Rust as even Java now releases once per six months.
In the case of Rust the fast feedback loop is facilitated by the `cargo check` command which halts compilation after typechecking. Unlike in Swift the typechecking phase in Rust is not a significant contributor to compilation times and so skipping code generation, optimization, and linking is sufficient for subsecond feedback loops.
I mean, you still need to run code at the end of the day. Yeah, the type checker will update your IDE quickly enough, but you still need to compile and link at least a debug build in order to meaningfully qualify as a feedback loop IMHO.
This was my initial mindset as someone whose background lies in untyped languages, but after time with Rust I no longer feel that way. My feeling now is that seeing a Rust codebase typecheck gives me more confidence than seeing a Python or Javascript codebase pass a test suite. Naturally I am still an advocate for extensive test suites but for my Rust code I only run tests before merging, not as a continuous part of development.
To give an example, in the past week I have ported over a thousand lines of C code to a successor written in Rust. During development compilation errors were relatively frequent, such as size mismatches, type mismatches, lifetime errors, etc. I then created a C-compatible interface and plugged it into our existing product in order to verify it using our extensive integration test suite, which takes over 30 minutes to run. It worked the first time. In order to ensure that I had not done something wrong, I was forced to insert intentional crashes in order to convince myself that my code was actually being used. Running that test suite on every individual change would not have yielded a benefit.
> This was my initial mindset as someone whose background lies in untyped languages
Yes, I understand and agree regarding Rust vs dynamic languages, but to be clear my remark was already assuming type checking. I still think you need a full iteration loop even if a type checker gets you a long ways relative to a dynamic language.
The +=1 in the above code is defined behavior. Unlike in C, the Rust compiler is not allowed to assume that overflow does not happen, and must restrict its optimizations accordingly. The undefined behavior in this code would be a result of the dereference in the next line. If there existed a check to ensure that overflow had not occurred prior to the dereference, then this code would be well-defined. And because overflow is defined behavior in Rust, the aforementioned overflow check could not be optimized away, as it could in C.
No, Rust does not allow safe conversions from integers to function pointers. The code `main as usize as fn()` will result in a "non-primitive cast" error. In order to convert from an integer or raw pointer to a function pointer, the unsafe function `std::mem::transmute` must be used.
In that case, then a linter warning seems more appropriate for pointer->int than requiring "unsafe". I feel "unsafe" should not be diluted to mean "unwise". But what do I know, I'm a C++ programmer...
The broader topic of whether it is safe, or wise, to cast between pointers and integers in general is an area of active research. Ralf Jung's blog is required reading on this topic: https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html
Rust quietly has several other features in order to improve the quality-of-life of its ownership model. Two examples: if you have a mutable reference then Rust will coerce it to an immutable reference if one is required, and if you have a mutable reference then Rust will transparently re-borrow it when calling functions that accept mutable references in order to allow you to use the mutable reference more than once despite the fact that they do not implement Copy.