Earlier quoted context omitted.
He says explicitly that these are endemic to memory-safe languages, including Rust . They aren't something that Rust handles better than Python or Java.
Even if that's true (and I have my doubts), it doesn't make those non-Rust languages safe.
Memory safety is necessary, not sufficient
151–160 of 162 posts
Re: Memory safety is necessary, not sufficient
#152> This leads to four approaches:
> * We do not break backwards compatibility, and we ignore the memory safety issue (this is the ‘do nothing’ option). > * We do not break backwards compatibility, but we try to make things as memory safe as possible given the constraints we have. > * We break backwards compatibility, and we try to make things as memory safe as possible but still remain unsafe. > * We break backwards compatibility, and we become memory safe by default.
> The first option is seemingly untenable, but is also where I see C heading....
> The second option is how I view at least one of the possible C++ successor languages, Circle.....
> The third option is where the most famous successor languages live: Carbon and cpp2....
> But what about a fourth option? What if someone did a TypeScript for C++, but one that was closer to what Rust does? You might argue that this is basically just Rust? (Interestingly, it is also sort of what Zig is with C: you can use the same compiler to compile a mixed codebase, which is close enough in my opinion.) What makes me worry about option #3 is that it doesn’t pass the seeming test that is coming: memory safe by default. I still think moving to a #3 style solution is better than staying with #1, but is that enough? I don’t know, time will tell.
When put that way, I can't help but wonder about approach #3; if you're already going to break compatibility, why not go the whole way and be memory safe by default? Even as someone who really likes most of the design decisions that Rust has made that are orthogonal to memory safety, and there's a lot of room for differentiation here. Offhand, some of the features of Rust (and its ecosystem) that I've seen people express a desire for alternatives on include:
* package management (centralized repos, lack of namespaces)
* scope of stdlib (relatively batteries-not-included e.g. regex, randomness APIs, http clients, async runtimes all being third-party)
* error handling (lack of exceptions, special operator to early return, somewhat prone to boilerplate like Ok-wrapping without resorting to heavy use of macros)
* integer semantics (no variable width or decimal type in std, overflow checked in debug but allowed in release mode, no implicit casting but lossy explicit casting allowed)
* type definitions (structs/enums with traits, no inheritance, reliance on the "newtype" pattern for certain things)
* function definitions (required type annotations for parameters and return values no overloading, no optional parameters, no variadic arguments)
* syntax/readability (ML-style type annotations, immutable by default, postfix operators like ? and .await, use of macros instead of functions for printing/formatting strings)
* concurrency design (built-in support for locks and 1:1 OS threading, de facto need for third-party runtime to use async, need for 3rd-party libraries for channels other than the deprecated ones in std, no "structured concurrency")
* opinionated APIs for allocations (heap pointers constructed via generic type wrappers, panicking by default if allocation fails, non-trivial boilerplate required to swap out allocator)
* linking (statically linked dependencies that don't use FFI, dynamically link to system's libc by default)
I have to cut myself off here because I keep thinking of more stuff as I write this list, and I probably wouldn't run out of ideas any time soon. I think there's plenty of room for design space for a systems language that's memory safe by default but doesn't look or feel anything like Rust by making different choices on some or all of the above concepts, and that's without even getting into the fact that the unsafety boundary itself can be defined in a variety of different ways, a point that the blog post makes after contrasting the way Java, Go, and Rust expose the ability to do "unsafe" programming:> If we think about all of these designs, they all are very similar conceptually: safe code is the default, but you can call into some sort of unsafe facility. And everyone is very clear on the relationship between the two: while the unsafe facility exists to be used, it must uphold the rules that the safe world relies on. And that means that safety and unsafety have a super/sub-set relationship. In the core is unsafety. But at some point, we draw a line, and on top of that line, safety exists.
Re: Memory safety is necessary, not sufficient
#153Earlier quoted context omitted.
> Conventionally, in software security, Python is considered a memory-safe language. The piece makes the case that Python isn't memory safe when you FFI into a C library. Interesting and largely unknown trivia: it's possible to invoke memory errors in the underlying C interpreter from pure Python code — no libraries and no imports needed! One way of doing this is by creating new `code` objects with crafted bytecode.…
Is this because of a bug and might be fixed in the future or is it considered an unavoidable consequence of some design decision and will stay that way for the foreseeable future? From what I understand about Rust, if something similar was possible in safe Rust it would be considered a bug and eventually fixed.
(1) disable creating new `code` objects directly from Python. This probably would break lots of things.
(2) Add a bytecode verification mechanism that would reject `code` objects whose bytecode would result in memory errors when executed. This could be a lot of implementation work; I'm not sure.
Re: Memory safety is necessary, not sufficient
#154Earlier quoted context omitted.
Undefined behavior is critical for performance. Without undefined behavior, C compilers would not be able to optimize at all. You'd be running everything at -O0 or worse.
> Undefined behavior is critical for performance Not only is this not true, it's trivially easy to prove it's not true. Both rustc and clang generate LLVM IR and use LLVM to optimise and generate machine code. The code that's generated is equally performant, as you'd expect since most of the optimisation is being done by LLVM, not the front end. The difference between the two frontends is that rustc is stricter, reje…
Re: Memory safety is necessary, not sufficient
#155Earlier quoted context omitted.
Undefined behavior is critical for performance. Without undefined behavior, C compilers would not be able to optimize at all. You'd be running everything at -O0 or worse.
The main performance-critical undefined behavior in C is provenance. The rest can be removed without major performance impact. (Which is not to say they can't give you 10% on specific workloads, just they aren't what is taking you from -O0 to -O3.) A related student poster from EuroLLVM 2023: https://llvm.org/devmtg/2023-05/slides/Posters/05-Popescu-Pe... It tests the performance impact of some of the secondary undef…
Re: Memory safety is necessary, not sufficient
#156Earlier quoted context omitted.
Undefined behavior is critical for performance. Without undefined behavior, C compilers would not be able to optimize at all. You'd be running everything at -O0 or worse.
Where's your proof? Rust has far fewer UB than C yet its performance is comparable to C.
Re: Memory safety is necessary, not sufficient
#157Earlier quoted context omitted.
Undefined behavior is critical for performance. Without undefined behavior, C compilers would not be able to optimize at all. You'd be running everything at -O0 or worse.
Undefined behaviour enables only a fairly small set of optimisations. There's a large set of optimisations that can be implemented completely safely without having to make such dangerous assumptions. Other programming languages do this all the time, it's not just C/C++ that have optimisers!
Note that I am not talking about UB like signed integer overflow. Removing that would slow down programs by a couple of percent. The important type of UB is pointer provenance. This ensures that e.g., writing to a random memory address is UB.
Re: Memory safety is necessary, not sufficient
#158Earlier quoted context omitted.
Undefined behavior is critical for performance. Without undefined behavior, C compilers would not be able to optimize at all. You'd be running everything at -O0 or worse.
Even if that's true (which is not given, as others have said), it would be a worthy trade to make. Software needs to do what it's meant to do first and foremost. Speed doesn't mean shit if you can't trust that the software actually works.
*((int*)rand()) = 42Re: Memory safety is necessary, not sufficient
#159Earlier quoted context omitted.
>Before Rust existed, it was seen as an inevitable issue that one must work with, not as a problem to solve. There were many memory safe languages before Rust, Java and C# evangelized this a lot, though at that time the accent was on memory leaks not on safety. Rust offers an alternative for low level programming and might be faster in soem scenarios then managed languages.
> might be faster in soem scenarios in which scenarios would it not be faster?
If you are interested into debating this with me and proving that Rust is the best I am not interested in a language war.
Re: Memory safety is necessary, not sufficient
#160Earlier quoted context omitted.
Undefined behaviour enables only a fairly small set of optimisations. There's a large set of optimisations that can be implemented completely safely without having to make such dangerous assumptions. Other programming languages do this all the time, it's not just C/C++ that have optimisers!
This is simply not true. Almost all optimisations rely on undefined behavior, see https://news.ycombinator.com/item?id=38760475 Note that I am not talking about UB like signed integer overflow. Removing that would slow down programs by a couple of percent. The important type of UB is pointer provenance. This ensures that e.g., writing to a random memory address is UB.
The trick is to define behaviour, which is what other programming languages do.
E.g.: in both C# and Rust, integers have fixed sizes. A C# Int32 is equivalent to a Rust i32. Only God knows what a C/C++ "int" is. It could have 17 bits and use ternary.