site banner

Culture War Roundup for the week of December 15, 2025

This weekly roundup thread is intended for all culture war posts. 'Culture war' is vaguely defined, but it basically means controversial issues that fall along set tribal lines. Arguments over culture war issues generate a lot of heat and little light, and few deeply entrenched people ever change their minds. This thread is for voicing opinions and analyzing the state of the discussion while trying to optimize for light over heat.

Optimistically, we think that engaging with people you disagree with is worth your time, and so is being nice! Pessimistically, there are many dynamics that can lead discussions on Culture War topics to become unproductive. There's a human tendency to divide along tribal lines, praising your ingroup and vilifying your outgroup - and if you think you find it easy to criticize your ingroup, then it may be that your outgroup is not who you think it is. Extremists with opposing positions can feed off each other, highlighting each other's worst points to justify their own angry rhetoric, which becomes in turn a new example of bad behavior for the other side to highlight.

We would like to avoid these negative dynamics. Accordingly, we ask that you do not use this thread for waging the Culture War. Examples of waging the Culture War:

  • Shaming.

  • Attempting to 'build consensus' or enforce ideological conformity.

  • Making sweeping generalizations to vilify a group you dislike.

  • Recruiting for a cause.

  • Posting links that could be summarized as 'Boo outgroup!' Basically, if your content is 'Can you believe what Those People did this week?' then you should either refrain from posting, or do some very patient work to contextualize and/or steel-man the relevant viewpoint.

In general, you should argue to understand, not to win. This thread is not territory to be claimed by one group or another; indeed, the aim is to have many different viewpoints represented here. Thus, we also ask that you follow some guidelines:

  • Speak plainly. Avoid sarcasm and mockery. When disagreeing with someone, state your objections explicitly.

  • Be as precise and charitable as you can. Don't paraphrase unflatteringly.

  • Don't imply that someone said something they did not say, even if you think it follows from what they said.

  • Write like everyone is reading and you want them to be included in the discussion.

On an ad hoc basis, the mods will try to compile a list of the best posts/comments from the previous week, posted in Quality Contribution threads and archived at /r/TheThread. You may nominate a comment for this list by clicking on 'report' at the bottom of the post and typing 'Actually a quality contribution' as the report reason.

6
Jump in the discussion.

No email address required.

Every language in which you can say the compiler/runtime - just trust me bro I know what I am doing will devolve into just trust me bro language. This is why you don't have trust me bro sections. So it may be me - but I never really understood the point of Rust. As in why it exists. To me it seems that it combines the clumsiness of golang with the unsafety of C.

Btw - both C and C++ are quite memory safe if you don't try to be clever.

Every language in which you can say the compiler/runtime - just trust me bro I know what I am doing will devolve into just trust me bro language. This is why you don't have trust me bro sections.

There is a name for a language which does not have 'just trust me bro' sections. It is Java.

If you want to do anything interesting with hardware or squeeze out optimal performance, you will sometimes end up in situations where you are making assumptions which can not be verified by the compiler, which generally is ill-equipped to verify arbitrary mathematical proofs or parse hardware specifications.

Ideally, a language would allow you to specify hardware behavior and include a theorem verifier which you can use to prove that because two variables are co-prime per your precondition, your divisor can indeed not be zero in the next line. Instead, you have unsafe blocks.

Of course, some lazy programmers will decide that unsafe blocks are the path of least resistance. Probably when C came out, some asm programmers decided that they could code "C" by just using inline assembly for everything. If you want to protect a programmer from harming themselves, you need to place them in a safe padded cell like Java does.

The use case of Rust is when you have someone who is actually willing to work with the borrow-checker and only use unsafe in the places where that is not possible. This will make it much easier to audit the code. Imagine having to verify the stories of two suspects. Suspect "Rust" provides you ironclad, notarized evidence for 90% of his claims, while 10% (the unsafe stuff) is unsupported by evidence. Suspect "C" provides you no evidence for any claims. To make sure that their story checks out on a similar level of confidence, you would likely spend 10x as much work on subject "C" (or possibly more because the unsafe code blocks can interact.)

Btw - both C and C++ are quite memory safe if you don't try to be clever.

For C, that is a ridiculous claim. You might as well say that the Taliban regime is great for women's rights as long as the woman is willing to submit to her husband and not voice controversial opinions.

Sure, there are plenty of programs in C which are obviously sound. But not every problem is easily transformable into such a program. "Don't be clever about memory management" is not actionable advice if you need to share data with indeterminate lifetime between multiple threads any more than "try to be straight and submissive" is actionable advice for an Afghan butch lesbian.

Array accesses in C are memory unsafe as fuck. Unlike for C++ (_GLIBCXX_ASSERTIONS), the best way to do safe array indexing in C boils down to "wait for clang to implement -fbounds-safety".

Any language with reflection, dynamic types at runtime and few other travesties Java supports is trust me bro. Same with dependency injection.

For C - I haven't written in a long time, but just moving to the bound functions solves a lot of errors. Now for multithreading and passing messages and data - the answer is use erlang.

There is a name for a language which does not have 'just trust me bro' sections. It is Java.

If I had a nickel for every java.lang.NullPointerException I've seen in the wild, I'd probably have at least a few more bucks, and I don't use that many Java applications.

I would argue that while null pointer dereferencing (at least in userspace) is bad, it at least of bounded badness, because it invokes well defined behavior, just like an integer division by zero. You could say that any language with runtime errors (or even any language where you do not have to prove the correctness of your program) qualifies as 'trust me bro', but that is very distinct from a memory-unsafe language.

Central examples of memory troubles, such as use-after-free or out-of-bounds accesses are much more evil because they do not invoke well-defined failure modes. Often, they lead to arbitrary code execution.

Just to be pedantically clear (I saw your comment on the chronological page and didn't realize it was correct in context until I looked at the parent), a null pointer dereference invokes well-defined behavior of bounded badness in Java. In C, a null pointer dereference is Undefined Behavior and so is still allowed to lead to arbitrary code execution, both in theory and in practice.

You are correct.

I would still say that in C, under a few circumstances you can depend on a null pointer access crashing, e.g. if all of the following apply:
(1) You are in standard userland where nothing is normally mapped to 0.
(2) You know that your code will not be run in other settings (for example, you are not writing a library).
(3) You are not handling untrusted data.
(4) You are not in a privilege elevated mode (like in the kernel)

Then you can usually count on getting a segmentation violation for null pointer access, so that failing to do

if (!ptr)
    abort();

will be of limited badness. (Given all these caveats, it is probably less bad

Compare and contrast with another source of undefined behavior: out-of-bounds array accesses. These will typically not cause segfaults, but will instead silently corrupt program data and flow, often leading to arbitrary code execution if exploited. It is the difference between getting killed from smoking and getting killed from smoking while filling up your gas tank.

Now, you could make the point that anyone treating a segfault as a safety net (instead of ruining one's day as much as one's car airbag firing) should not be programming with raw pointers, and I might even agree. In my defense, my baseline is physicists who self-taught C while programming with ROOT, and most of whom have no business coding in any language unsafer than Python, who still happily use C arrays and for whom a segfault on every third compile is just normal.

I would still say that in C, under a few circumstances you can depend on a null pointer access crashing, e.g. if all of the following apply:

(5) Your compiler hasn't (ab)used the fact null pointer accesses are undefined behavior to "simplify" code in unexpected ways.

Eg, consider

int foo( int *p )
{
  if ( p ) function_with_side_effects();
  return *p < INT_MIN;
}

Since p is unconditionally dereferenced in the return statement which results in undefined behavior if p is null, the compiler is free to assume that it is never null and generate code that unconditionally calls function_with_side_effects() rather than inserting a branch as if ( p ) is only ever not true in the presence of undefined behavior. That is, it can treat the code as if it were instead written as

int foo( int *p )
{
  function_with_side_effects();
  return *p < INT_MIN;
}

Depending on exactly what function_with_side_effects() does, that could result in a lot of unsafe outcomes even in "unprivileged user code that never handles untrusted data". Even more fun, since *p < INT_MIN evaluates to 0 for all possible values of *p, the compiler can completely remove the dereference that you'd expect to cause a segfault, thereby treating the code as if it were written as

int foo( int *p )
{
  function_with_side_effects();
  return 0;
}

Thanks, that is a good point.

(Also, I am pretty sure that compiler developers who use undefined behavior that way to 'optimize' code will go to hell eventually, but that will not help me if I am stuck debugging such a thing.)

C# added nullable reference type annotations a few years ago, and if you're strict about actually using them (my Indian coworkers are not, sadly) then you can reliably eliminate 99% of null reference exceptions. When the C# team finally gets around to implementing discriminated unions (in the next decade or three) and we finally have Option<T> there will be actual null safety in C#.

Java does have a trust-me-bro mode. See https://developer.android.com/reference/sun/misc/Unsafe

That's Android documentation, but regular Java has the same facility.

Granted, Unsafe is being deprecated, but we'll have equivalently powerful FFI stuff.

C++ now has smart pointers and one you get the hang of them, you don't want to go back to the old way of managing memory manually. It's not about the language being intrinsically "safe" or "unsafe", but rather that it enables you to automate memory management and you don't have to think about it unless you absolutely need to. You can just have a small "just trust me bro" section instead of having the cognitive load of having to double check the entire codebase.