5 ms·
Yeah I think "better C" is a space which has a really good reason to exist and I'm always interested to see new entrants. IMO describing Rust as a "C replaceme
by skohan 7y ago
Yeah I think "better C" is a space which has a really good reason to exist and I'm always interested to see new entrants.
IMO describing Rust as a "C replacement" is slightly off the mark because Rust's value proposition is very different from C. Rust is about giving you the best possible performance in a safe-by-default language. C is about giving you maximal control over memory, with a very thin layer of abstraction over the hardware.
C has traditionally been used in a lot of places where Rust's USPs add a lot of value -- for instance in kernel development -- simply because there wasn't a safe alternative. However I think there are other cases where the strengths of C still add value; for instance in game development you're largely trying to do high-throughput processing over large swaths of structured data, and Rust concepts which help safety like RAII just get in your way. Yes there are ways to get around this in Rust, but Rust is not really optimized for structured, manual memory management.
As a result I think there is plenty of room for something like C which just has better ergonomics and some more modern features.
- WalterBright 7y agoD also now supports Ownership/Borrowing (experimental) on a per-function basis, meaning it can be added incrementally to an existing program.
- dgellow 7y agoFor reference, the blog article on borrowing/ownership from 2019: https://dlang.org/blog/2019/07/15/ownership-and-borrowing-in-d/ https://dlang.org/blog/2019/07/15/ownership-and-borrowing-in...
- exiled13 7y agoD's borrow checker fails in comparison to Rust's. It serves very little purpose and provides almost no guarantees. These issues have been brought up multiple times but they are brushed aside with ignorant responses like "I've been told my entire career what I'm doing will fail but I continue to do it anyways".
- WalterBright 7y agoYes, you do create accounts to follow D around and complain about it.
- exiled13 7y agoI'm Datman.
- throwaway17_17 7y agoWhat is the difference between the two?
- exiled13 7y agoRust was designed from the ground up and every feature is implemented and designed to work with its borrow checker. So it is able to provide a definitive guarantee that memory isn't free'd or used after it is free'd. The only exception is in "unsafe" code, where you can break the borrow checker. But this limits where you have to worry about potential bugs. In D, the ""borrow checker"" is being tacked on as an after thought, in an attempt to copy Rust. This means that it doesn't play nice with existing features and makes it difficult if not impossible to guarantee that memory isn't leaked or used after it was free'd. For example with exceptions. The checker doesn't check for exceptions and if memory is free'd correctly if an exception is thrown. This isn't a problem in Rust because it doesn't have exceptions so it doesn't have to worry about checking them so it can maintain its strong guarantee.
- cesarb 7y ago> This isn't a problem in Rust because it doesn't have exceptions Rust does have something similar to exceptions when compiled with "panic=unwind" (the default). It uses the same mechanism as C++ exceptions to unwind the stack (while calling all the necessary destructors), can be caught (std::panic::catch_unwind) and rethrown (std::panic::resume_unwind), and has some of the same concerns as C++ about "exception safety" (mostly within unsafe code - the programmer has to take care to leave the objects in a safe state when it can unwind).
- pjmlp 7y agoThe only place where I think C on kernel will always have a place is on UNIX clones, they are just too symbiotic. So as long as UNIX clones exist C will be around. OSes with POSIX APIs is a different matter, because there the kernel can be something totally different, e.g. mainframes. So for that, we still need something like Checked C. Or as Oracle, ARM, Google are doing, hardware memory tagging.
- neutronicus 7y agoRust is essentially C++ with auto-generated Move Constructors, and compiler errors on use-after-move, at the cost of preventing library developers from providing function overloads that eat temporaries as an optimization. So, yeah, really not a C replacement at all imo
- oconnor663 7y agoI'm not super familiar with r-value references, but doesn't passing-by-value in Rust do essentially the same thing as that optimization does in C++?
- neutronicus 7y agoYes. But this is all a bit uglier at the call site, since either the library provides value-semantically-similar things with different names that either eat their arguments or copy-from-reference them, provides only the argument-eating version and relies on the caller to `clone` at their discretion, or provides only the referencing version and fails to elide copies. In C++ you can provide a referencing version, and an argument-eating version with the same name that is called automatically when the user gives it a temporary or specifically requests it via `std::move`. Automatically eating temporaries is very nice in the case where the caller would like to compose a bunch of "create-new-from-a-set-of-references" operations in a single expression to create one new thing from an initial set of references. The canonical example is eliding copies in stuff like B*(A*x + b) + c with overloaded * and + for vectors, since you really don't want to use something with a name other than + to request copy-elision. If you are one of those people who is grumpy about operator-overloading, you can imagine doing this with other "copy-some-refs-create-something" type functions, ... but actually you are probably grumpy about overloading those too.
- oconnor663 7y agoThere is a Rust idiom that works a little bit like that overloading case: fn eats_a_string(s: impl Into<String>) { dbg!(s.into()); } fn main() { eats_a_string("foo"); eats_a_string("foo".to_string()); eats_a_string(&"foo".to_string()); } In that case, eats_a_string() will allocate internally in the first and third cases, but it won't allocate in the second case, because the caller gives up ownership of its own allocated String. I wouldn't say this is a super common idiom, and "provide only the argument-eating version and rely on the caller to `clone` at their discretion" is often preferred to be simpler and more explicit. But you see it occasionally in the standard library, for example here: https://doc.rust-lang.org/std/ffi/struct.CString.html#method.new https://doc.rust-lang.org/std/ffi/struct.CString.html#method...
- fluffything 7y agoMany programmers think of the programming world as: "assembly < C < everything else", and that is a misconception that was actually quite easy to "learn" if one wasn't ready to invest a lot of time in looking for more options. When I see someone saying that they prefer D/Nim/Zig value proposition as a "better C" than Rust, what I see is that people are just starting to realize that there are many more options than C for doing programming tasks that require a more precise interaction with the hardware. It's just not "assembly", but there are multiple different assemblers with different trade-offs. It's also not "assembler < C" anymore, since in some aspects newer languages do offer more low level control about certain things than C. It's also not just C/C++/Zig/Rust/Nim/D competing at the lowest-level of the space. Ada, Forth, Scheme, and many other languages are also widely used in this space. They just aren't the "next new thing".
- pjmlp 7y agoI think that turn over happened when a new generation started to learn computing after UNIX took over the server room and teachers that didn't care to teach anything else. So only the old generation remembers how things used to be, and those that are curious about the computing history and evolution of programming languages.
- kllrnohj 7y ago> However I think there are other cases where the strengths of C still add value; for instance in game development you're largely trying to do high-throughput processing over large swaths of structured data That world is dominated by C++ for the most part, though. So D's -betterC would be fighting against an incumbent that is itself also already a "better C". What value does D's -betterC bring to the table here? Skimming through it, it doesn't look like there's any compelling reason to switch to -betterC from an existing C++ code & knowledge base.
- skohan 7y agoI think a lot of people would argue that many C++ features, like OO, are not strictly improvements. Also C++ is a massive language, and part of the beauty of C is it's simplicity. I think what a lot of people would want is something which maintains that simplicity, but mainly delivers on basic QOL lessons we've learned in the past 40 years, like that it's nice not to have to pass around array lengths as separate variables.
- kllrnohj 7y ago> I think a lot of people would argue that many C++ features, like OO, are not strictly improvements. Sure, but D has those OO features, too, so that's an argument against both, not one. > like that it's nice not to have to pass around array lengths as separate variables. -betterC doesn't have dynamic arrays, though. That's one of the "unavailable features" currently. But that's also sommething C++ solved as well - std::vector does exist, after all, as does move semantics to avoid copying it unnecessarily. C++ is far from perfect, of course, but D's -betterC doesn't really fix any of C++'s issues and it doesn't retain C's simplicity. It's in an awkward middle ground between the two - is that really a viable place to be? For users that already know & have C++ codebases, what's the sales pitch here? For existing C users, what does -betterC do that hasn't already been done and already failed to attract the C holdouts?
- pjmlp 7y agoOne thing that D does better and that on C++ depends pretty much on the team and which compiler toolchain you use, bounds checking for strings and arrays. You can get them in C++, and VC++ does it by default on debug code, but not all compilers do by default and it isn't required by the standard unless you explicitly make use of the at() variants. On the other hand, every attempt to bring bounds checking to C has failed, lets see how far Microsoft goes with Checked C.
- msla 7y ago> C is about giving you maximal control over memory, with a very thin layer of abstraction over the hardware. Which suggests an avenue I haven't seen anyone take yet: Pick a subset of features common to sane hardware and write a language which gives access to those features in a way which is reasonably portable and as explicit as C. Pick a spot midway between a macro assembler and a language with a complicated optimizer and give as much access to the hardware as possible while not wedding yourself to a single ISA. Make cross-compilation a first-class feature using a module system, and error out if the programmer tries to use SIMD intrinsics when compiling for an MSP430 or something.