Hacker News

Favorites Setup
Comment by phamilton | original | C for Rust programmers
[−]phamilton · 2026-10-10 Sat 14:03 UTC · link
Re: arrays are pointers.

One of my favorite things to show just how bare this is in C is to show array access commutativity.

  char c = {1,2,3}

  c[1] == *(c + 1)
  
  *(c + 1) == *(1 + c)
  
  c[1] == 1[c]
C is wonderfully simple at times.
[−]mr_00ff00 · 2026-10-10 Sat 14:28 UTC · link
All fun and games with arrays being pointers, until you declare an array in a function and return it.
[−]jackling · 2026-10-10 Sat 14:36 UTC · link
Isn't this easily catchable with static analysis, -Werror -Wall? I've never had a practical problem with this.

Also arrays aren't pointers in C, they decay into pointers. You can see this since sizeof will work differently in the function that instantiates the array versus one that takes in the pointer as a parameter.

[−]Ygg2 · 2026-10-10 Sat 14:33 UTC · link
Isn't Lisp even more bare bones?

At end of day minimalism is a neat but not decisive feature. If minimalism was decisive we'd all be writing Brainfuck.

[−]brabel · 2026-10-10 Sat 14:43 UTC · link
Lisp minimalism is very different. It assumes a runtime with automatic memory management for example, even if the language concepts themselves are minimal, especially in Scheme, it has almost no syntax and just a few core facilities on top of which everything else is built, which are closely related to the Lambda calculus, kind of ignoring completely what real computers actually look like.

In C the minimalism comes from providing only the minimal set of things that are available in most (maybe all) architectures (the Von Neumann paradigm), like linear memory, a simple function calling convention, close mapping to Assembly operations etc. But C syntax is not very minimalist compared to Lisp , let alone Forth. The fact that C syntax became prevalent in the programming world seems to be mostly an accident to me, it’s not objectively better than those minimalist languages’ or Pascal’s, Prolog, ML families.

[−]Ygg2 · 2026-10-10 Sat 18:04 UTC · link
> In C the minimalism comes from providing only the minimal set of things that are available in most (maybe all) architectures

By that logic Brainfuck is even more minimal. Again. I'm saying minimalism isn't the goal. It's a good quality but not most important one.

[−]brabel · 2026-10-11 Sun 08:36 UTC · link
You just misinterpreted what I said. I was referring to a minimum common denominator of cpu architectures. Brainfuck has nothing to do with that.
[−]WalterBright · 2026-10-11 Sun 00:07 UTC · link
> providing only the minimal set of things that are available in most (maybe all) architectures

The rise of C in the 1980s induced CPU makers to design instructions that cater to C semantics.

[−]xtajv · 2026-10-11 Sun 08:17 UTC · link
That is because C already worked everywhere even before CPU makers agreed on things like making pointers and words the same size.

Edit: And by "worked everywhere" I mean C did the heavy lifting of being a programming language that was even a little bit portable across architectures.

[−]pjmlp · 2026-10-11 Sun 09:46 UTC · link
PL/I had more knobs for portability, but neither Multics, nor IBM Oses were available for free.
[−]uecker · 2026-10-11 Sun 08:42 UTC · link
I don't think this is really true. What instructions would this be?

In contrast, Intel introduced a lot of instruction for PASCAL, e.g. enter / leave etc. which are basically unused.

[−]rramadass · 2026-10-11 Sun 03:10 UTC · link
> In C the minimalism comes from providing only the minimal set of things that are available in most (maybe all) architectures (the Von Neumann paradigm), like linear memory, a simple function calling convention, close mapping to Assembly operations etc.

This is beautifully illustrated in the old classic The C Companion by Allen Holub which overviews a simple abstract demo architecture and code generated for it. This is a small book (in the vein of K&R C) but contains enough illuminating info. for a programmer.

[−]KerrAvon · 2026-10-10 Sat 14:58 UTC · link
I would add on top of brabel's post that C is surprisingly difficult to parse correctly (C++ notoriously so).

Zoom out a little: C is a lot like Unix: simple probably isn't the right word; `underengineered` comes to mind. Which leads to complexity, as you need to make things work in the real world. And so Unix syscalls being designed in the early 1970's for a PDP-11 don't really map to modern needs. And so every unanalyzed complex C app has memory leaks.

To be clear, I like C, I like C++, I like Objective-C, I like Swift; I'm comfortable in all of them. But, in 2026, I don't see why for native code everyone shouldn't be programming in Rust / Swift / other modern memory-safe flavor for any new production use. Zig if you want faster compile times, I guess.

[−]skydhash · 2026-10-10 Sat 15:36 UTC · link
> But, in 2026, I don't see why for native code everyone shouldn't be programming in Rust / Swift / other modern memory-safe flavor for any new production use. Zig if you want faster compile times, I guess.

Because C is very simple (unlike Rust) and works everywhere. Zig is not yet stable. Go and Swift are under the governance of tech companies.

The true appeal of C for me is the standard and how it applies only to the language. You can easily take a project from 2 decades ago and port it to a current platform. Lot of current ecosystem is way too fussy about tooling to do this.

[−]tialaramex · 2026-10-10 Sat 20:23 UTC · link
> Because C is very simple (unlike Rust) and works everywhere

That's definitely true. C is a "Worse is better" language and as such it's everywhere. You can knock together a halfway usable C for some crap hardware in a few weeks and then the hardware is saleable because there's a C implementation.

> You can easily take a project from 2 decades ago and port it to a current platform.

Much of the software I wrote two decades ago in C won't even build today. Good luck figuring out why, periodically I try to figure out which weird 2000s era Mac OS hacks don't like a 2026 Linux system and eventually I give up and write modern software instead.

In theory C written in 2006 definitely "just works" on a 2026 Linux machine but in practice real world C is broken and even though I (co)wrote it I don't know why. Also in practice the first serious Rust project I wrote in 2021 I just found it, checked out the oldest working version ("first rough working code" says the log) from git, cargo run, works as expected.

Maybe it's because the C was four times older, but I think it's because in the real world you don't end up writing that mythical portable C code too often.

[−]fastaguy88 · 2026-10-11 Sun 01:55 UTC · link
I have 'C' code that was written in 1985 that runs today, mostly after moving function parameters from inside the function to inside the argument list.

'C' seems to be a language that makes it easy to write incomprehensible statements, but it's not that hard (or it was not that hard) to write code that is trivial bring up to date. Perhaps it was easier for me because I started with Fortran.

[−]fithisux · 2026-10-11 Sun 05:16 UTC · link
Very common.
[−]msla · 2026-10-11 Sun 08:04 UTC · link
I will never understand why some people put C in quotes.

Do you also put Java in quotes? Rust?

[−]pjmlp · 2026-10-11 Sun 09:52 UTC · link
Only if it targeted UNIX in first place, or was only making use of standard library.

I am quite sure my MS-DOS C code is going to have some hard time being compiled on GCC 16.

[−]lelanthran · 2026-10-11 Sun 05:56 UTC · link
> Maybe it's because the C was four times older, but I think it's because in the real world you don't end up writing that mythical portable C code too often.

It's because C, over time, went from a bunch of almost compatible implementations, to a standard that differed a little from every existing implementation, to a standard that is updated every decade or so, which is then implemented by a bunch of different products, with enough ambiguity in the standard (UB) that there will be differences between compilers and versions of compilers.

Rust is a single implementation. Always has been.

It's a single product, so you code to the product. The product has no external forcing function, like a language standard, that produces changes every decade. When you write Rust, you aren't thinking "Wait, lets make sure this works on the Watcom compiler too".

If Rust code cannot compile 30 years later, that's a failure on the language, because the single implementation is in full control of compatibility. If C code cannot compile 30 years later, that's not a failure of the language, because the implementation in 30 years has had external pressure forcing changes.

People often forget that Rust is a product, C is a specification.

[−]tialaramex · 2026-10-11 Sun 09:00 UTC · link
> a standard that is updated every decade or so

In practice the cadence is exactly half that of WG21's breakneck C++ pace. Every six years. C11, C17, C23 so far and C2y is expected to land before the end of this decade.

But anyway, all I can do is report the observable fact. If you want to say it's because GCC 15 is a different product from GCC 4 whereas rustc 1.99 is the same product as rustc 1.49 then I guess you can re-phrase my position as "In C you will keep needing new products and that sucks" if you like.

[−]pjmlp · 2026-10-11 Sun 09:50 UTC · link
In theory, in practice many C developers only know "My C Compiler standard", and are usually surprised that some of their expectations aren't even part of ISO C PDF specification.

Then they get irritated that not all C compiler vendors have the same language extensions or implementation defined behaviours they got used to rely on.

[−]jstimpfle · 2026-10-11 Sun 10:34 UTC · link
True. In C, it's hard to know what's defined where and what dependencies one is even using unknowingly. The only help is to build on multiple platforms from early on, this will reduce the pain of porting to new platforms later.
[−]jstimpfle · 2026-10-11 Sun 09:03 UTC · link
It's _very_ easy to write C that is impossible to port to other platforms or other compilers. The reason is that more than for most other ecosystems, there are many different implementations with their own implementations and quirks.

It's also easy to write C that won't build on the system it was originally developed on, a few years down the line. But that is true for almost every other language ecosystem. I would say, C makes it _possible_ to write code in such a way that it works on many different platforms and compilers, and/or can continue to work in decades to come with relatively little maintenance.

The latter isn't true for most other ecosystems, because to get anything done in them, you need to rely more on that language and its ecosystem -- specific language features, specific libraries. C isn't a glue code language. It's very good at letting you create your own thing.

To do it well though, requires a lot of care and expertise.

[−]creata · 2026-10-11 Sun 09:22 UTC · link
> more than for most other ecosystems, there are many different implementations with their own implementations and quirks.

Is that the reason? I thought it was primarily because C stupidly* makes a ton of platform-specific things more convenient than the platform-independent equivalent - the width of integer types, locales, etc., all vary by platform, and it's easy to accidentally depend on them.

* Maybe it wasn't stupid at the time when C was developed, but it's a bad choice now.

[−]jstimpfle · 2026-10-11 Sun 10:31 UTC · link
Integer widths are a problem, but hardly the biggest headache (unless it's a decade old preexisting project that was never designed with portability in mind). Fixed width integer types like int32_t have existed for decades. Even Rust has usize which is machine dependent, and pointers as well have to be machine dependent of course.

Much bigger problem is understanding the scope of dependencies. Dependencies can go bad and you need to update. A dependency might not be available on some new platform so you might have to replace with something else on that platform. To make the software portable, good modularity is required. This is to a large extent an aspect of software architecture. Even Rust can't magically make this happen, if you depend on 300 crates that's probably not a great place to be in either if you want to be portable and maintainable.

There is probably a point that C's flat namespace is a major contributor to badly designed software, because people aren't aware of the dependencies they're mixing all the time. Also C encourages transitive includes, leaking implementation details to the user instead of just to the compiler. (But btw. I find C++ and Rust namespace to be unergonomic syntax-wise, and what's needed is not actually namespaces but control over visibility).

[−]uecker · 2026-10-11 Sun 09:08 UTC · link
It is "underengineered" perhaps if you believe it is the job of the language to prevent memory leaks (which Rust also doesn't). IMHO it is just fine - and in fact better - if the language is small and analysis is done by a separate tool.

I also do not see why the simplicity of C should lead to complexity. I have build complex systems in C. I did this using C++ in the past, but switched to C because I found out that all the complexity of C++ was a huge distraction and not something that helps me design better systems. C++ also came with a lot of claims that all the features are absolutely needed to build good abstractions and that this is not possible in C, something I found to be completely false. (And then, a lot of complex and still extremely reliable software I use daily is build in C. My practical experience completely contradicts the myth propagated nowadays by some that all C is inherently bad and unreliable . In my experience it is the exact opposite: The C software I use is in fact the most reliable.)

My view is that Linux and free software is still going strong, because it managed to largely keep out the unnecessary complexity out of the core infrastructure. With Rust and AI, I worry that now get a lot of overly complex infrastructure and that it will be hard to ever get this complexity under control again.

[−]jhgb · 2026-10-11 Sun 09:33 UTC · link
> But, in 2026, I don't see why for native code everyone shouldn't be programming in Rust / Swift / other modern memory-safe flavor for any new production use.

The lack of OpenMP for Rust etc. seems like the most obvious reason for "why use C instead of Rust in 2026" to me.

[−]pwdisswordfishq · 2026-10-10 Sat 15:03 UTC · link
I have known about this for quite some time, and the more I think about it, the more useless it seems. Sure, the underlying machine operation is just addition, which is indeed commutative, but at the type system level, the pointer and the offset have distinct roles. It just makes no sense to allow to commute them, just like it makes no sense to allow to commute arguments to, say, strchr, just because the compiler can figure it out by looking at the types. When was the last time you had a practical reason to write "offset + pointer" or "index[array]"?

In most languages, the indexing operator is not commutative. In Rust, pointer offseting is expressed as a function call or a method, also not commutative. I have never seen a single complaint about either. It's not something people want or care about, it's just a tedious detail.

[−]skydhash · 2026-10-10 Sat 15:29 UTC · link
> It's not something people want or care about, it's just a tedious detail.

It’s not something idiomatic, but indexing in C is syntactic sugar. Not sure why they allow it in the syntax, but forgetting that arrays are pointers and not special type is just asking for bugs.

[−]im3w1l · 2026-10-10 Sat 15:42 UTC · link
Arrays are not pointers in c. Though they are very similar and will implicitly convert, there are differences. The big and obvious difference is that declaring an array will allocate space for it. In a function, on the stack. In a struct, inline. They have different sizeof. I think there are also some other stuff I can't remember.
[−]ynik · 2026-10-10 Sat 18:08 UTC · link
In C there's only two operations you can do with arrays that do not decay to a pointer: `sizeof(array_var)` and `&array_var`. The latter produces a rarely-seen "pointer to array", i.e. a type like `int (*)[10]` (pointer-to-array syntax works like pointer-to-function syntax).
[−]uecker · 2026-10-11 Sun 08:39 UTC · link
Luckily I start to see such pointers used more often, because such pointers can be used for bounds checking because the size is preserved in the type.
[−]skydhash · 2026-10-10 Sat 15:25 UTC · link
> C is wonderfully simple at times.

Yesterday, I watch a quick video[0] where Matthew Butterick was comparing book sizes and their appeal. “The C Programming Language” was my second programming book (after one about JavaScript 1.x) and I still remember it fondly. Easy to start with (with CodeBlocks on Windows and gcc on Linux) and the concepts were nicely explained. The book were also very nice.

[0] https://www.youtube.com/watch?v=W-ryv6TwQvM

[−]creata · 2026-10-10 Sat 15:36 UTC · link
As fun as it is, 1[c] will be "marked obsolete" in C29 according to Wikipedia.
[−]avadodin · 2026-10-11 Sun 06:38 UTC · link
C11 is last C as far as I'm concerned.

I don't mind adding new features in committees but if they are going to remove compatibility, they should rename their language to nuC or CantbelieveitsnotRust(yet).

No offense intended to Rust which is a fine language.

[−]teo_zero · 2026-10-11 Sun 07:09 UTC · link
If C was not supposed to be changed to correct its past mistakes, we would still increment with a=+1 instead of a+=1, or we would use something called nunununuC.

> C11 is last C as far as I'm concerned.

Are you sure? I think C23 brought so many enhancements it's a pity to ignore it.

[−]xtajv · 2026-10-11 Sun 08:21 UTC · link
> we would use something called nunununuC

"C++", perhaps? :)

[−]xtajv · 2026-10-11 Sun 08:13 UTC · link
I'm with you there.

> if they are going to remove compatibility

C as a language has one job, and it is to not do this.

[−]pjmlp · 2026-10-11 Sun 09:44 UTC · link
So you still code in K&R C, I reckon.