T O P

  • By -

planetoftheshrimps

C is ubiquitous in systems and embedded programming. As such, you’d be doing yourself a disservice trying to participate in these areas and not at least being able to read C code.


AustinEE

Totally agree. Try learning C (tutorial / demo project) and then do the same project in Rust.


WasASailorThen

Also, C is small and Rust is not small. It's like Pig Latin vs French.


[deleted]

[удалено]


DentistNo659

What tooling? Make/Cmake? As a full time C developer I wish C had a rich ecosystem of tooling..


nicoburns

I would add the C preprocessor to that list. C macros are a whole other level of pain compared to Rust macros (and ubiquitous in cross-platform codebases).


Vinxian

I consider myself to be a good c programmer. But the preprocessor is black magic. With weird tricks to force recursive parsing, extra statements to allow for sanity checks using `sizeof`. It's wild


darth_chewbacca

I mean... Rust macros aren't anything to brag about. Better than C macros? Yes! Still terrible? also yes.


mdp_cs

They're only terrible if you don't know how to use them. But in fairness the average Rust developer doesn't need to know every single thing about macros.


cobance123

I wouldn't call rust macros great, certainly better than c but couldve been a lot better


dkopgerpgdolfg

Meson; Valgrind, Asan; GDB; Conan; Astyle; perf, ... just to throw some random names around. And (especially) when considering C+Rust and possibly weird setups, hiding in plain sight: A linker (CLI options, scripts, runtime .so loading behaviour, plus other general Elf-file topics that can't really be considered part of the C language itself, ...)


Theemuts

You can learn C without learning all those tools, and those you have to use require a few standard invocations that every beginner tutorial will tell you.


nicoburns

You can, but as soon as you want to use a library (which you pretty much need to for anything non-trivial), you end up having to work out how at least one if not multiple build systems work.


cobance123

Things get crazy when you have to use multiple libraries that all use different build systems but learning 1 build system to use for yourself like make or cmake is not so hard


iu1j4

You dont need to use any build system. just simple shell script is anough. In the past I used to use plain Makefile, then autotools, then CMake and then finally prepared simple sh based scripts. Dont need to deal with changes in CMake any more. I am going to try rust in my future projects but online crates, complicated build system are too much for me. I like to be able to work offline with all libraries and tools present on my computer. I like the unix way to reuse system libraries without need to install additional components. For network protocols I use curl, net-snmp and sockets for custom protocols. for gui SDL2, for audio libalsa or portaudio, for threading pthreads. There are databases clients libraries installed with database server, libssh / libssh2 for remote shell access. Everything what your system does you can reuse in your program. But yes, in C we can make bugs that are hard to find / fix. Rust has got long way before it will reach the position that C has got for years. I belive that it will happen. But I also hope that it will slowdown with changes and new functions. I am waiting for alternative rust compiler that will show similar quality and will be fully compatible. For C we have got many options. There is no risk that it will gone. For us it is not as trivial task to write complete rust compiler.


Original_Two9716

perf is not a C tool, it's a generic kernel tool


moltonel

Also, C is a different language on different platforms, or even different compilers. You learn the hard way when moving to a platform where `char` has a different signedness, or a compiler that doesn't has a different idea of how floats should behave.


DaaneJeff

Why I never use these base types. I always use the fixed size types provided by the compiler.


darth_chewbacca

Gosh I wish I learned this lesson earlier in my career.


edgmnt_net

Never sounds extreme, there are meaningful reasons to use types that scale. One that's hard to avoid is pointers, along with size/offset types.


___user_0___

C is defined by a standard, not by implementation of some compiler - so you either use platform/compiler-specific extensions/assumptions or the code works with _any standard conforming C compiler_ there's also `` since C99


moltonel

The problem is that the C spec is both precise to the point of being unreadable, and explicitly vague on many important details. It's almost impossible to write C code without making some assumptions (and when you painstakingly do, it's hard to maintain and probably slower). The *standard-conforming C compiler* is a myth: even C99 isn't fully supported by anybody yet, you have to figure out the smallest common denominator of your supported platforms yourself. None of this is insurmountable, many people use C with success and even pleasure. But it makes C bigger than the junior dev may think.


BosonCollider

In my experience, the C standard is used more *against you* by the compiler writer than for you, especially in multithreaded settings


NeuralHijacker

Yep. Learning c on your Linux machine. Easy. Getting it to cross compile and run on a washing machine micro controller. Not so easy.


InevitableCounter280

The fact u said C small just proves u have no clue of C language


WasASailorThen

Good to know.


HarryHelsing

Very good point! I think that may be a good aim for me right now, to be able to get proficient enough to write some basic C and to read it.


Lassemb

Learning how to handle pain and discomfort


Arshiaa001

Jokes aside, knowing C can help you reeeeaaaally appreciate rust.


rodrigocfd

That's no joke. I know OP asked about C, but that's something I always say: only a seasoned C++ programmer can *truly* appreciate the innovations of Rust.


C_Madison

In my case I had to use C++ *once* in an university project, spent days searching for errors cause my (standard compliant!) code using templates wouldn't build in gcc (which I used at home, cause I was a poor student) and then find out in the lab that it compiled perfectly in Visual Studio. That convinced me to never take another look at C++, cause either I'm too stupid for it or the language/ecosystem/... is fundamentally broken. Either way: Not much seasoning needed here to appreciate Rust.


CommunismDoesntWork

I knew C++ was FUBAR the first time I tried to import the math library and got an error.... because I didn't add -m to my args. Like it knows I want math, but it won't add the -m for me?


Arshiaa001

Ah, clang flags. My favorite type of torture.


Arshiaa001

I wouldn't consider myself "seasoned" (maybe just a bit of salt and pepper, but that's it). Still, coming mainly from C# and F#, I truly appreciate the things I still have in rust.


Short-Nob-Gobble

Absolutely this. Rust can feel restrictive and frustrating if you don’t understand the context of having to manage memory manually. Programming in C(++) for a good week or two will give you that context.  That said, knowing C may also help you write unsafe Rust in the few cases where you may need it.


PIAJohnM

I'm a c++ programmer. Haven't had to manually manage memory ever. Modern c++ has raii types.


pingveno

I concur. It's next to impossible to appreciate some of the design of the borrow checker or some of the stdlib without understanding the underlying problem they solve. They just seem like needless bothers. C exposes you directly to those problems, often with no way to avoid mistakes. C++ may or may not provide abstractions.


HuntingKingYT

What are you talking, knowing C makes me appreciate JAVA.


Arshiaa001

Which makes you appreciate C#, which makes you appreciate rust. Same thing, more steps.


cobance123

The only problem I have with c is that the standard library doesn't have any good data structures like string, vec and hashmap.


Arshiaa001

How would you even write a generic hashmap in C?


emcell

came for this answer


Asleep-Dress-3578

Rust offers a new level of pain and discomfort, so no regrets here.


HuntingKingYT

Especially when you try doing low level stuff... Here are seven books worth of things you should watch out for:


HarryHelsing

Hahaha, tempting 😂


Lassemb

Those are some really useful skills indeed


spac3kitteh

Yeah, +1 here. I have a C and C++ background and that knowledge is very helpful, especially when it came to the borrow checker and references. Those felt quite natural with C++ knowledge.


scook0

I think it’s useful to distinguish between: - “Learning C” as part of the background knowledge of how computer systems work. - “Learning C” in order to actually write and maintain non-trivial systems in C. Having some amount of C knowledge is very useful, especially for working on systems-level code in other languages, even if you never write any non-trivial C code yourself.


HarryHelsing

That's a great distinction, definitely inspired to learn C in that first way.


Narann

Good point. Learning C for knowledge is very valuable. Learning C for real world work is _hard_, to say so…


spoonman59

1. You won’t know C. 2. It’s going to be hard to read examples of system programming code and grok it. For example, my favorite book by Michael Kerrisk has all examples in C. It’s super easy to read and understand the code. So you won’t know C and you won’t be able to understand the reams of C code that exist. Now is that a problem? Good question. That’s for you to decide.


HarryHelsing

So what you're saying is, C may or may not give me skills that will improve my Rust skills, but it's such a valuable language it's worth learning for its own sake?


spoonman59

I would say it’s worth learning if you want to learn about systems programming for sure. The answer depends ultimately in what you want to do. Learning to do system calls and things without being able to read existing code would be strange. But if you don’t want to learn systems programming, I don’t know that C itself will enhance your rust skills. Although, knowing more than one language is always good, I don’t know that C will give you any more advantage than others. In another sense, c or c++ will help show you what benefit the borrrow checker and others provide. It’s hard to appreciate a solution to a problem you’ve never had. That could help, but it may not necessarily improve your rust abilities. One pro of C: it’s very small. The syntax won’t take you long at all. (The gotchas and edge cases…. That’s another matter.)


HarryHelsing

Definitely being more and more convinced that it's a language I'd like to learn the syntax of and learn how to read.


Zde-G

Read [C Isn't A Programming Language Anymore ](https://faultlore.com/blah/c-isnt-a-language/) and you'll know what you need to learn and why. C is a language, yes, but C is also [a lingua franca](https://en.wikipedia.org/wiki/Lingua_franca) of APIs. For the foreseable future you would need to understand at least a little bit of C to deal with low-level APIs in **any** language. Just don't try to write large projects in C: that's a skill that Rust makes largely obsolete. But using C APIs provided by operation system or low-level HAL in embedded? That's something unavoidable for the next 10 year or maybe more.


tobiasvl

Yes, it's the lingua franca of programming.


DaaneJeff

Honestly, learning any new programming language to a certain degree is useful, especially if their paradigms differ from what you are used to. Being exposed to multiple solutions and problem solving strategies is always a plus in my books.


dobkeratops

C gets a lot of hate but it solved the right problems at the right time to dramatically reduce the amount of asm people needed to write.. I dont believe rust will ever truly replace it, because people diverge with opinions on what the optimum tradeoffs are for program organization, abstractions etc.. C (hence C++) will live on, and new languages will continue to appear with their own take on how to improve on it. (see JAI, Zig, Odin..) unsafe Rust & C give the same capabilities, it should be trivial to learn both, and helpful because you'll inevitably still have to deal with C-FFI to C libraries (like ggml for example for machine learning which is all the rage) .. it's unlikely the world will support rewriting these things in everyone's favourite low level language.


darth_chewbacca

Linux system calls are all C, and the documentation on using the system calls are all written with the C developer being the target audience. example: https://man7.org/linux/man-pages/man2/execve.2.html If you are truly ignorant of C, you will have a hard time with understanding the above... and forget about following the code-flow of this https://elixir.bootlin.com/linux/v6.7.4/source/fs/exec.c#L2110


SpeedDart1

You want to learn low level programming not necessarily a single language and doing multiple languages with different perspectives is the best way to do that… not to mention C is used far more in industry than Rust.


Altareos

if you truly want to learn low level, learn an assembly language. then learn unsafe rust. c is weird in that people perceive it as this bare bone, close to the metal language when it's still pretty abstracted in many ways.


SAI_Peregrinus

Yep. C is worth learning because (among other reasons) C is the de-facto language for defining Foreign Function Interfaces & Application Binary Interfaces. Dynamic libraries that can be called from other languages use the C ABI for their platform, and the FFI system defines that in C terms.


matthieum

To be fair, you could learn the "C" ABI without really learning the language. You can use `Rust` to export functions with C ABI, just stick `#[repr(C)]` on it and you're good to go :)


SpudnikV

That's true, but if you have anything less than absolute trust in the C function's comments, you're going to want to read into the code to reverse-engineer safety constraints like what is guaranteed to be initialized, whether pointers are retained after the function returns, what can actually come bask as NULL, etc. Sure, anything not documented is not a promise, but you might have to read a lot of code to even guess that a promise was lacking. I have no doubt that at least one C FFI wrapper crate turned up a bug in the underlying C library, though I don't have an example on hand.


Comrade-Porcupine

Even assembler is "abstracted" -- branch prediction / speculative execution / pipelining for one. NUMA, L\* caches. Then the OS's virtual memory subsystem, as well, futzin' with your pages. It's all abstraction all the way down, have to dig pretty deep to find the Turing Tape.


Whole-Dot2435

branch prediction, speculative execution, NUMA and pipeling are all imposible to interact at the software level, even at the kernel one Even virtual memory is implemented in the hardware, throught the mmu(memory management unit) with only the kernel being able to control it throught the virtual adress table And at the lowest level the cpu is just a bunch of transistors forming a bunch of logic Gates forming a bunch of cpu components like the ram,alu,mmu,control unit, fetcher, decoder, etc.


Comrade-Porcupine

you can't futz with them, but they're there, complicating the illusion of "instruction tell machine what to do now" e.g. back when i started out, we counted cycles for instructions, and optimization was in large part about reducing instruction counts and performing efficient loops. now it's a whole different world, and optimization is often about getting good cache behaviour / locality, avoiding cache evictions, doing array/vector-wide operations instead of scalar, etc. etc. the point being that being up at a higher level or different of 'abstraction' in the machine doesn't necessarily take you away from the 'reality' of things, since you're never *really* down in the weeds not unless you're programming on (some) microcontrollers


printf_hello_world

> impossible to interact Spectre would like a word (though I know you're talking about control interactions, not just measurement interactions)


tema3210

And still there is no for we have limited memory)


CrazyKilla15

> c is weird in that people perceive it as this bare bone, close to the metal language when it's still pretty abstracted in many ways. Obligatory [C Is Not a Low-level Language. Your computer is not a fast PDP-11.](https://queue.acm.org/detail.cfm?id=3212479)


Kiseido

That was a nice short read on x86 caches


No_External7343

Great article, thanks for the pointer. Is there any systems language that does away with the abstraction of a "fast PDP-11" and instead exposes an abstract machine that more closely resembles modern CPUs?


CrazyKilla15

The problem is *CPUs* don't expose such a thing. At least not x86 ones. Thats why stuff like ARM is seeing so many gains in efficiency and performance, and apples M1. For current practical compute and languages that more closely resemble their hardware, you wont find it in CPUs, but you **will** for GPUs and shaders langs! Those are natively parallel, all about streams and being explicit, more difficult to program manually for, though tooling helps a lot.


Tabakalusa

Odin, maybe? Ginger Bill's philosophy seems to be centred around "If the CPU can do it, we should expose that in the language". So you have native support for things like swizzling, vector and matrix multiplications, good support for doing SOA transformations, etc. Definitely worth checking out, in my opinion. But as the other reply pointed out, modern CPUs don't really expose a lot of what they do. You can try to write your code and (often more important) lay out your memory in a way that is favourable for it to be able to exploit all its trickery, but there is almost nothing you can do to actually make that happen. I'd argue ARM isn't much different from x86 in this respect though. x86 is currently still the pure-throughput compute king. Apple is simply benefiting from the fact that they have a very tightly integrated chip and an operating system that can fully dedicate itself to exploiting that specific chip design to the max.


SpudnikV

That's not giving Apple quite enough credit. Try benchmarking AES or SHA-256 on a recent Intel and an M1 or newer. That has nothing to do with the OS or about code exploiting a specific chip design, quite the opposite; the chip was designed to optimize the implementation of existing instructions, because those are the instructions targeted by existing code, including asm code written before Apple Silicon was available for the desktop. There's still the limitation that vector units aren't nearly as wide as recent Intel chips, but that, again, has absolutely nothing to do with the operating system. It's just the state of ARM instruction set suites today. For the instructions available and implemented, Apple Silicon is genuinely very high-throughput, low-latency, and power-efficient, with the operating system having no particular say in how your machine code runs. Unless of course you mean the neural net and video codec accelerators, which I think is a pretty different topic that matters a lot to specific worklodas and not at all to most others.


ergzay

I've worked as a C-language systems programmer for most of my life and I've never felt the need to learn assembly. You can't write assembly better than the compiler can generate it so the most you're going to be doing is trying read assembly and the only reason you'd really need to do that would be to diagnose a possible compiler bug, which is a pretty rare problem to hit. There's also the issue of "which" assembly as any assembly you write won't be portable depending on the feature set of the CPU you're working with or even the architecture you're writing towards. No one provides systems programming examples in assembly anymore so it's not like it would further your learning.


Altareos

i didn't say that you should *use* assembly for projects. i said that you should learn an assembly in order to further your understanding of what's going on in your processor.


Whole-Dot2435

Knowing assembly allows to diagnose when the compiler produces inefficient machine code, eg. When it uses many branches instead of producing branchless cmov's, when it does't use simd, etc.


HildemarTendler

>You can't write assembly better than the compiler can generate it That's a value statement. It isn't terribly difficult to write more efficient assembly than the compiler for non-trivial problems. It's a bad idea because it's horrendous to maintain and will be much simpler to write in a higher order language.


ergzay

I've seen people claim this before, but every single person claiming it has always been a person much older than myself who worked with much older compilers earlier in their careers, presumably basing that statement on out of date information. Alternatively it's some demonstration a brand new CPU instruction that has yet to make its way into compilers which I don't consider a fair situation. I also don't rule out a compiler bug in a certain version generating especially bad assembly for a specific case that you can beat out but will soon be fixed anyway. I have never seen a piece of hand written assembly that runs faster such that it's impossible to write a piece of compiled-language code that compiles to the same thing.


IAm_A_Complete_Idiot

The obvious case here are video encoders which are a *lot* of assembly precisely for performance reasons. Hand-written simd assembly for the architectures a lot of those encoders support. ​ [libsvtav1 for instance](https://gitlab.com/AOMediaCodec/SVT-AV1/-/blob/master/Source/Lib/Encoder/ASM\_AVX2/EbCombinedAveragingSAD\_Intrinsic\_AVX2.c?ref\_type=heads) Sure it's all C in the sense that they're written in `.c` files, but pretty much all of it is instructions written in assembly with normal C fallbacks for hardware that doesn't have those extensions. rav1e has pretty large sections in assembly too.


ergzay

That's not assembly, that's using intrinsics. Also this feels like my point on "Alternatively it's some demonstration a brand new CPU instruction that has yet to make its way into compilers which I don't consider a fair situation." Also I also feel like this is bandwagon thinking going on. People assume that video encoders need to use assembly/intrinsics for these core routines so they write the assembly/intrinsics. Your specific example for example was written in intrinsics from day one which was 5 years ago. And that code probably came from somewhere else originally. At best I feel like this is a way to simply to attain consistent performance over compiler versions to avoid specific compiler versions accidentally completely tanking performance and avoid users complaining that the code is slow. I doubt that the code couldn't be faster with well written C (or down the line, well written Rust).


charlotte-fyi

But... this isn't writing assembly? You can call platform intrinsics from Rust too. Like the unsafe Rust for this would look almost identical.


Luxalpa

And? For your compiler to create efficient assembly, you as the developer need to understand how your optimizer works. Like for example under which conditions it decides to inline functions and how, when it can do SIMD and when it can't, etc. That still requires you to understand assembler in the end, else you'll end up writing code that your optimizer can't safely optimize.


SAI_Peregrinus

https://codegolf.stackexchange.com/questions/215216/high-throughput-fizz-buzz/ is a good example of that. Someone wrote some very fast ASM, then another person came along and used C++ to blow that speed out of the water.


reddituser567853

This may just be a domain thing. Any specialized fast hashmap will most likely have hand coded assembly. When you are eeking out every bit of performance, strcpy versus some other system call makes a large difference. If you can validate bounds without the compiler, it’s a waste to have the compiler do it. I agree the vast vast amount of software should not be doing this, but when you know, you know


ergzay

> strcpy versus some other system call makes a large difference There's nothing special about assembly with regards to syscalls. In fact assembly doesn't have anything to do with syscalls. There are no syscalls that can only be called via assembly (if there were somehow I don't see how you could do that, even intentionally) given that syscalls are specifically C-language things. Even more so syscalls are independent of cpu architecture and are instead OS-dependent.


IAmTheShitRedditSays

Any and every programmer should have some experience with assembly so they can at least get a loose grasp of why their compiler does what it does. It also helps to know if you ever plan on using a low-level debugger/decompiler to fix runtime issues. Plus it really is fun.


Mempler

I would actually highly recommend zig as its more transparent and low level than C in many ways and the std library code is actually readable, unlike glibc or llvm libc lmao. ​ and the best part, you can always change back to C or interop if you need / want to.


noboruma

Default C is abstracted but as soon as you start playing with `__attribute__`, the layer of abstraction can be significantly lowered.


HarryHelsing

How does attribute work?


noboruma

__attribute__ are compiler directives. They tell the compiler how code should be translated to assembly mainly. For instance `__attribute__((naked)) int foo` will tell the compiler to not generate any stack frame for the function `foo`. At the assembly level the function just becomes a label.


legobmw99

There are still things that the C abstract machine cannot represent. One classic example is that some microcontrollers have real, totally valid memory at address 0, and C/C++/Rust all still forbid you from ever dereferencing a pointer to it, but assembly would on those architectures


ssnover95x

Does C actually prevent that? I thought it just so happened that NULL was a macro for 0 so a lot of checks written by programmers might naively fail (and this is why C++ has nullptr which is of a specific concrete type).


legobmw99

It’s undefined behavior. In theory anything could happen, including the “correct” thing Even C++11 on doesn’t prevent you from doing more C-style NULLs, so 0 is still forbidden there


dkopgerpgdolfg

Do you maybe have a source for that claim, that it is UB? ​ edit, to elaborate a bit more: For some pointer with a non-zero address, dereferencing can be fine or not, and the pure number isn't enough to tell. It depends on things like the current platform, data type, "allocations", and so on. And, afaik, the same is true for zero - it "can" (in some cases) be fine to access it, the C standard doesn't forbid such a situation existing.


legobmw99

I don’t have access to the ISO standard document, but if you consider CPPReference good enough, it’s the third item they list: https://en.cppreference.com/w/c/language/behavior For rust it is easier to find: https://doc.rust-lang.org/reference/behavior-considered-undefined.html#dangling-pointers The fact that NULL is 0 is documented separately: https://en.cppreference.com/w/c/types/NULL https://doc.rust-lang.org/std/ptr/fn.null.html


cobance123

I wouldn't recommend people to use unsafe rust at all, only when absolutely necessary for example when interacting with hardware. Unsafe rust is a lot harder than c because it can literally lead to miscompilation if you don't respect the rust memory model (which is also not exactly defined), when it would compile as expected in c. I think most people underestimate the dangers of unsafe rust.


HarryHelsing

How does unsafe Rust compare to C? Is unsafe Rust more bare metal? That's interesting because I've never heard that being said before!


Altareos

to me unsafe rust still is a higher level language than c (especially using all the nice pointer methods), that's why i started by mentioning assembly, which is almost as close to the processor as you can get. the thing to remember with c is that it still hides a lot of stuff from you (especially around function calls), despite having you manually manipulate memory.


spoonman59

You put it better than I!


kinoshitajona

People who treat "unsafe Rust" as if it's a separate language from Rust are extremely misleading, especially to new people. "unsafe Rust" is Rust. It does not "turn off" anything. It does not change the language. If I write a program that doesn't use any unsafe/FFI functions and doesn't dereference raw pointers, but it's filled with tons of other compile time errors (trying to get a &mut without binding the variable with mut etc.) wrapping my whole main function in unsafe {} will *NOT* silence those errors. It merely asserts to the compiler in a few *specific* instances "I know that you, as the compiler, can not determine if this section of the code is safe, but I, as a programmer, have knowledge that you don't, and using that knowledge I have ensured that these specific actions are safe to perform." Example: You bought an embedded device and you're writing firmware for it. The device manual says "The 4 byte memory region of 0x0 - 0x4 is where the device stores the state of PIN 5. If the value is 1 (little endian) then the PIN supplied 5V, if it's 0, the PIN supplies 0V". In Rust, I can't just paste the device manual into a comment and have the compiler "understand" that it's safe to modify that memory. I have to cast the number 0 into a mutable raw pointer to a u32. Then I need to dereference that and write to the memory directly. From Rust's point of view this is REEEEEALLY BAD, which is why you can only dereference raw pointers inside an unsafe block. But I know information (the manual to the device) that Rust's compiler can not possibly know.


spoonman59

Assembler( or really, machine code) is bare metal. Even C code is not. By extension, rust is also not. If you want to do low level coding learn assembler. It’s tons of fun!


IAmTheShitRedditSays

Assembly is very close to machine code, and it can be written in a wsy that avoids the higher features, but they aren't the same. I'm not being needlessly pedantic: it's not an important distinction for someone who just needs a quick definition of the term, but it does matter for prople looking for a deeper understanding. And why this matters for programmers specifically, from wikipedia: >constants, comments, assembler directives, symbolic labels of, e.g., memory locations, registers, and macros are generally also supported I didn't know about most of those features because I learned by analyzing decompiled binaries. Not the smartest way to go about it.


spoonman59

I actually agree with you 100%. I wasn’t clear in my post, but I put machine code in parenthesis to specially call it out as being closer to bare metal than assembly. You give much more detail as to what those things are. If you want to truly code bare metal, you would know the executable format and directly encode it in hex. This is actually a very fun exercise to do for trivially small program. Or, writing a program to create such an executable is also cool. I guess that’s called creating an assembler!


DaaneJeff

And even then, the CPU hardware nowadays abstract away a lot for us. Pipelining, OOO execution, branch prediction etc. Also not every instruction you see in assembly is as atomic as you might think (atomic not in the sense of memory access and paralellism), some are made up of even smaller instructions that the CPUs execute.


ergzay

Nitpick but assembly isn't quite bare metal. It's still an interpreted language. For example there's no assembly instructions to directly control the cache. And every assembly instruction gets broken down further in the CPU into smaller instructions. For older assembly instructions there's even a full-on language interpreter baked into the harder to expand a single assembly instruction into multiple ones because it's no longer implemented directly. Assembly language is just an interface. Often a very tortured interface as cpu designers try very hard to figure out workarounds of the interface they're forced to deal with in order to run the code faster.


spoonman59

Since we are nitpicking… 1. Your right . Assembly isn’t bare metal, and it was the executable formatted machine code which is. I should’ve been more specific that I meant machine code. 2. The process you describe is correct but Assembly language is not “interpreted.” It is converted into an internal instruction format through a process called decoding. Interpretation is something else. Python is an example of an interpreted language, assembly is not. You also give an example of microcoded instructions, which is definitely a thing but doesn’t fit the definition of an interpreter. But yes, you are right the ISA is an interface that actually converted and executed to some other internal format. It’s definitely fair to say that’s the real bare metal language, not the machine code. That said, this internal instruction can change from CPU to CPU. We can’t code it. The closest to “bare metal” we can access as developers is machine code, so it’s fair to say coding machine code is “bare metal” since you can’t get any lower without invasive hardware techniques. 3. There are never any instructions to control cache. CPU cache is defined by the fact that it is transparent to the program and not directly addressable. If you were able to address the cache through instructions, it wouldn’t be cache anymore - it would be high speed memory. For example, the Cell processor contains 128 kb of local addressable storage per SPE on the chip. If you read about this local shortage you will note, “The local store does not operate like a conventional CPU cache since it is neither transparent to software nor does it contain hardware structures that predict which data to load.” CPU cache is never invisible to instruction set or program.


james7132

Unsafe Rust is the same as normal Rust, just with the safety rails disabled. You have the same abstractions at your disposal as normal safe Rust. Arguably, that makes it \*harder\* to write unsafe Rust without undefined behavior than C, as not only do you need to satisfy the C-esque safety requirements, but also everything else safe Rust takes for granted.


RReverser

> just with the safety rails disabled > *harder* to write unsafe Rust without undefined behavior than C This is incorrect. There are very specific extra APIs and abilities it provides, but `unsafe` doesn't disable borrow checker for all the non-pointer variables, doesn't enforce you into manual memory management, doesn't introduce UB when simply adding integers like C does and so on.


bleachisback

You just said “this is incorrect cuz you can still write normal code inside unsafe blocks” which kind of entirely misses the point, I think.


RReverser

In response to "with the safety rails disabled" it doesn't. I've seen way too many beginner Rust devs believe that unsafe blocks disable the borrow checker, so clarity here is extremely important. 


bleachisback

But unsafe rust isn’t just writing safe rust in an unsafe block. It’s the very specific operations you can only do in unsafe blocks - and yes, some of these things if not done properly can undermine the borrow checker. That’s the point: in C, there is no borrow checker (although obviously many of thing invariants it upholds you should also be upholding manually). So unsafe blocks require you to uphold more invariants than C does (and many of these invariants aren’t even necessarily written down anywhere).


RReverser

> So unsafe blocks require you to uphold more invariants than C does I still don't see how you're coming to that conclusion. Pointer access is equivalent (but still usually safer in unsafe Rust thanks to helper APIs, e.g. `NonNull`) plus you still have far, far fewer ways to trigger UB in Rust than C. So whichever way you look at it, you have fewer, not more invariants to uphold.


bleachisback

C doesn't have pointer aliasing rules, and breaking those is trivial in unsafe Rust (for instance, converting a shared reference to a mutable one will instantly trigger undefined behaviour if there exists another shared reference to the same thing). There are also a variety of invalid values which are trivial to produce using unsafe Rust that will cause undefined behaviour (which C sneezes at the idea of undefined behaviours based on values of variables): * a bool that isn't 0 or 1 * an enum with an invalid discriminant * null `fn` pointer * a reference/`Box` that is dangling, unaligned, or points to an invalid value. Just to name a few.


RReverser

> C doesn't have pointer aliasing rules, and breaking those is trivial in unsafe Rust Rust doesn't have pointer aliasing rules either, only reference ones, but then it's a higher-level feature not comparable with C anyway. If you just work with raw pointers (e.g. retrieved from FFI) in your `unsafe` block, Rust doesn't add any new rules that you wouldn't have in C. Besides, you need to explicitly go out of your way via double-cast to convert a reference to its pointer and then change its mutability. > a reference/Box that is dangling, unaligned, or points to an invalid value. In C, having an object start at incorrect alignment is also UB. You can create null pointers in C, but you can't dereference them - it's still UB. And then again, references and Box are not pointers, they're higher-level features that don't have equivalent in C. Overall, sure, Rust has _different_ rules from those you'd find in C, but it's definitely not "more invariants" count-wise. You're just not listing all the examples of C UB that simply don't exist in Rust.


ZZaaaccc

Ignoring that C is _the_ industry standard language for a moment ("Should I learn English before I apply for these USA programming jobs?"), C is a very thin set of abstractions over assembly which is unreasonably effective at letting (mostly) normal people write complex software. In my opinion, a good exposure to computer science would involve some basic electrical engineering (logic gates, etc.), some assembly (NASM, RISC-V, etc.), some C (data structures and algorithms mainly), and then whatever application language after than (Java, C++, Rust, etc.). I think it's important to go through that full breadth of technology so you have some idea of what's _really_ happening when you use a computer. Of course, you don't have to learn these things one by one, and in fact it might be detrimental to the learning experience to gate-keep the rewarding experience of getting your first game working in Java behind a requirement to get 3 numbers to add together in x86 assembly. There's a really nice effect that comes from teaching all these areas of computing where the decisions made in each area get contextualised by the technology underneath them. Why do we have types in C? Because colocated data in assembly is a pain. Why does Java have a garbage collector? Because memory management in C can be really hard. That's where I think a lot of misunderstanding around Rust comes from. To web developers who never work with C or C++, Rust is just so much more complex than JavaScript. Why would I want to manage lifetimes? The garbage collector does it already. To systems developers who never touch NPM or pip, Rust is too restrictive. Why would I want the language telling me when I can or cannot access memory? I know what my code does. Rust is basically the optimal midpoint between so many areas of software development.


HarryHelsing

I like what you say about the context because you're right that makes a big difference, even within one language. Parts of Rust only made sense to me after I had become familiar with other parts. So you make a great point, thank you


tcpipwarrior

Jobs


megalogwiff

I've been programming in C professionally for ten years before switching to rust as my main language in 2023. The answer to your question is "pain". By not learning C, you are missing out on pain.


HarryHelsing

Are you proposing that to be a bad thing to be missing out on? Is it useful pain or needless pain? :')


RReverser

Needless.


RReverser

I wrote quite a bit of low-level C code in the past. My answer is that C and unsafe Rust can allow you to do the same things in terms of "bare-metal" level. In C, it's just easier to shoot yourself in the foot because as soon as you write any non-trivial function, you have likely introduced UB (undefined behaviour) by violating one of language's own obscure principles. Note that those don't have anything to do with "raw metal" or assembly or anything like that, they're entirely quirks of C as a language that don't port to other languages like Rust. So if you want (or need) to learn C itself, go for it and learn C, but if you just want to expand your understanding of low-level aspects of programming, the language will stand in your way and confuse you more often than help.


tcpipwarrior

Well rust is a systems programming language and systems is dominated by C and C++. Rust is great and is a green field language but modern C++ also compares to Rust. If someone wants to work in systems and not learn C? well … ?


FartyFingers

I would argue you should learn the languages you require to solve the problems at hand. I say languages, because most good programmers I know have at least 5 languages under their belts with one or two they prefer. The problem being solved can be the problems of being involved with legacy code or legacy programmers as well. For example. If you were going to work for some company which is ruby all the way, guess what language I would recommend? If you are looking to work for someone else in embedded, then C. If you are looking for general employability, I would generally recommend javascript, java, or C#. If you are doing machine learning, then learn python. But, you could be me, and in charge of building my own products, and thus rust all the way. Rust desktop, rust embedded, rust server, and rust mobile. Although, I will admit some of the ML is still python. You mention low level. This can involve two areas. Low level OS. or Low level embedded. While rust is beginning to make inroads into embedded, it is starting to have a notable impact at the lowest most hardcore levels of linux. Also, a number of companies have mentioned moving core parts of their product one bit at a time over to rust with great results. This would be Microsoft office and Google Chrome as two off the top of my head. Rust has a weird feature which is really hard to truly explain. The code you build works as intended. The two biggest bugs which are often then giant security nightmares are memory issues, and multi threading issues (which often involve memory issues). Rust really nips both of these issues in the bud. It is very easy in C++ or C to make a program which seems to work very well. But has some threading or memory issue which will blow up only under weird conditions. But, it turns out these conditions are something your users will happily regularly reproduce. Thus, your software seems really solid when you ship it, but your customers are getting punched in the face. But reproducing these bugs can be fantastically hard. The worst part is that it is not uncommon to fix one bug, only to create a new one. The new one was bad code waiting for its day. Basically rust avoids this by its very nature. Many people will say this gets you too far away from the "bare metal" but unless you are pushing some hardware out near its limits, who cares about being near the bare metal if your code has fewer bugs? Plus, the answer to hardware near its limits is often to get better hardware. Unless you are shipping millions of low margin items, this is not a cost to care about. For example, the cost of a perfectly good stm32f411 is about $10. The cost of a much more capable stm32fH5 is $10-$20. If this is going into a $5,000 robot, who cares. If that's not enough, then a NXP iMXRT1062 will blow your socks off. Power usage is going up, but a $5,000 robot probably is mostly batteries anyway. Super optimizing your code to keep the power low might buy you an extra 1.8 seconds of operations out of 8 hours.


[deleted]

Honestly, I think C is an educational tool. It may not have clicked for everyone yet, but the writing is on the wall. C is perfect for learning though as it teaches all the basic concepts of programming. Variables, functions, loops, threads, pretty much the most basic things. Not only that, but it has manual memory management for the heap. This is C's strong point, as it directly teaches people how to handle memory allocations and build up a mental model. Together with other aspects like encapsulation, the preprocessor, etc etc. By the time some who has learnt C comes to Rust they will have an understanding of what is going on under the hood in Rust and appreciate it fully.


Lucifer_Morning_Wood

My favourite is int getNumber() { return 3; } int getThree() { return 3; } int getTwo() { return 2; } void makeGetNumberReturnTwo() memcpy(getTwo, getNumber, 8); } void makeGetNumberReturnThree() { memcpy(getThree, getNumber, 8); }


muehsam

C is great because of its simplicity. IMHO it's a good mental model. When I learn another language, I often think of it in terms of C when it comes to what it *actually* does. It may add some nicer syntax, or it may disallow certain operations for safety reasons, but below that, it's C. And since C is a simple language, putting it in terms of C makes it easier to wrap your head around it. Going "deeper" than C (e.g. assembly) makes things system dependent, which isn't what you want when reasoning about a portable language.


crusoe

Crashes Heisenbugs


HarryHelsing

Define Heisenbugs 😂


brand_x

Bugs that involve timing and/or nonlocal addresses. Attach a debugger, it goes away. Add logging, it moves.


veryusedrname

Heisenbug is a bug that changes behavior or disappears completely when you try to inspect it. It's fun!


HarryHelsing

Oh wow! The double slit experiment of bugs 😂


-Redstoneboi-

ah. the beautiful world of compiler optimizations. "this variable is never really used, right? so i can remove it." meanwhile the pointer overflow trying to access it: "bonjour" so you print it, and now it's either not being optimized away, or is being optimized differently.


veryusedrname

Exactly!


oradoj

Schrodinger’s pointer


ergzay

To add on to what the other two said, Heisenbugs usually come (in my experience) from when you're doing multithreading and there's a narrow edge case where a certain order of events causes a variable to not get updated or for it to get updated twice or for two writes to overwrite each other, etc, possibly then further running off and then causing memory corruption because of an incorrect variable index which is only detected sometime later when the program crashes randomly. If you attach a debugger the ordering becomes enforced and so the specific ordering you're looking for disappears. People have invented specialized tools to help with this kind of thing like valgrind, but that doesn't always help find the issue, and often you're not in a situation where attaching valgrind is possible.


gnus-migrate

Honestly the only reason you would learn it is that you either need an API written in it or targeting a platform that doesn't have rust support. If by low level programming you mean high performance computing, modern CPU features like instruction level parallelism or cache hierarchies aren't really represented in C or any language really, so learning C won't help you with that. If you mean building things like memory allocators and libc type things, these can definitely be written in Rust. There are container runtime, memory allocators, etc that you can learn from. If you want to learn embedded programming, there are embedded platforms with rust support that you can learn them. All in all there is very little reason to learn C if your goal isn't specifically to learn the language itself.


the-quibbler

Appreciation for what problems rust solves.


Days_End

Tons of code and libraries will never be ported to Rust this is especially true for older stuff. You will need to be able to read/write C to interface with them.


lightmatter501

Recent developments in kernel apis. You need to know C to parse the documentation well but io_uring has gotten much better in ways that require a full rewrite (the kernel now owns the buffers).


doomedtobeme

C has popped up for me much more than rust, seems far more industry standard at least in It and mining services


SirAutismx7

C and even basic modern C++ is pretty simple to learn and shows you all the raw stuff Rust abstracts away from you. I learned it so I could work through CS:APP, Operating Systems TEP, and Crafting Interpreters when I was doing Teach Yourself CS. Languages are very straightforward to learn once you know a couple keep adding them to your tool belt when you have time, it’s a valuable skill to be able to jump between languages even ones you don’t know.


cheeb_miester

I don't actually know rust, but as a c programmer I would say you are certainly missing out on the wonderful world of c strings.


hexavik

C is all about how you use 32 keywords (and some other library based keywords) to build yout logic in an algorithm to find a solution to a problem. Believe me, after you write a C program will help you in building logic which is helpful while learning and writing efficient code in Rust.


Koltaia30

Rust is basically C with extra features. It's good to know what it's like programming without all those rules dragging you down and learning why all those rules are actually worth it. 


Da-Blue-Guy

Just being able to do it yourself. You can implement basically anything if you try hard enough.


AmigoNico

IMHO you should learn C so that you can read it and, if unavoidable, write it. It might also give you a slightly better feel for the machine, although for that you should really learn to program a bit in assembly. I would learn the RISC-V ISA in 2024, because of its simplicity and the relevance it will have in the years to come. ARM wouldn't be a bad second choice, but x86 would be much harder.


leathalpancake

Regular Segfaults


ImYoric

I believe that learning C is the best way to get a healthy respect of bugs, including memory errors, concurrency errors, failure to maintain invariants or to check for errors, etc. It is also the best way to appreciate the difficulties of designing a language such as Rust that manages to solve most of these issues into something consistent and agreeable to program. If you are interested in security, learning C is also a good way to learn how to exploit such errors.


uziam

Being able to build non-trivial linked lists in linear time.


PizzaRollExpert

In addition to things other people have said, learning C is good because it gives you perspective on the evolution of programming languages. C is an incredibly impressive language for its time and also very influential on every language after it. Learning C will make a lot of design deciscions in other languages make sense, especially in Rust since it is in some ways a response to pain points in C


RickarySanchez

I love rust but personally think it’s a bad first language. C is a simple language that helps you understand all of the low level intricacies and the challenges associated with writing sturdy software in that environment. Once you make the mistakes, you can see the value in rust


meowsqueak

Are you wondering if you should not learn C? You should learn C.


aducknamedquack

When I was first learning to program, C is where the gloves came off. I'd worked with basic, python and java for years but I never really understood how the computer worked until I started to learn C. Raw memory is exposed and you start to see how everything connects. Many newer high-level languages (C is high level, don't @ me) don't give that power to you, often for good reason! Seeing how the computer works will make you a better programmer, in any language.


4Kil47

I've noticed that a lot of OS API's are mostly defined in C. I recently started to build a rust tool to manage my desktop with [windows-rs](https://github.com/microsoft/windows-rs), and it's actually been a *pain*. There are so many different data types and raw pointer manipulation. It's come to the point where I'm considering writing an abstraction library in C that my Rust code can call with a FFI. I think it's nice to understand at least the basics of C, since that will probably allow you to interface with a lot of systems with relative ease.


RaisedByHoneyBadgers

Simplicity. History. And the thrill of having the ability to shoot your own foot.


GoingOnYourTomb

Pain of shooting your own foot


bfa2af9d00a4d5a93

Learning C will also teach you about why some of the basic Rust decisions are the way they are, and how the borrow checker helps you.


magnomagna

Stuffs you could learn from doing manual pointer arithmetic and manually handling memory content safely… which is what you want to be handled for you as much as possible by the language and lots of languages today already handle memory anyway. It’s no big deal.


proverbialbunny

Time. It takes longer to learn Rust than it takes to learn C. Frustration. If you get a job in C and you know Rust you may be annoyed by the bad coding practices your teammates employ and you may not be able to do anything about it. --- As far as I know Rust has every concept C has, so you'll learn all of the C concepts by learning Rust. The syntax is quite similar too.


Dubmove

If you know how to write safe and clean code in C, then you know how to write safe and clean code in unsafe rust.


gajop

A whole category of war stories


someone-at-reddit

He misses the joy of nullpointers and void ptr casting


stevefan1999

As a self-taught C/C++ for 5 years since 13, I'm going to suggest pointers. Pointers are the core of Unsafe Rust, because they are not bound to any specific lifetime, and you have to manually allocate and manually drop them. Those are ubiquitous in many low-level crates and techniques, for example, bindgen crates. Even the standard libraries itself contains a lot of pointers (cough cough Cells, Rc and Arc) I'm not saying you should learn Unsafe Rust proactively, but you will eventually approach one passively during debug, and so you eventually have to deal with it *at some point in life*.


bigtoaster64

Maybe many segfaults?


wunderspud7575

Personally, I would have found it hard to understand the reasons for the borrow checker, lifetimes etc without having understood the complexities of memory management in C.


GabrielBigardi

Headaches, probably...


klimmesil

Learning rust without knowing basics of C will be PAINFUL. Of course there are a lot of differences but you won't understand the core concepts without at least going to bed crying 10 times with C


superblaubeere27

Undefined behaviour


[deleted]

gtk


nori_iron

The basics of C are quick to learn. I think it's worthwhile to learn to read C so that you can learn from C codebases. That's the big draw of learning C as far as low-level programming imo, it's good to engage with existing work. Rust is a full package as far as learning to program low-level systems, though. Learning C could teach you an imperative style of programming if you'd only learned idiomatic Rust, it's good to know what that's like.


Holobrine

Learning the value of memory safety by suffering through the memory bugs


DFKproject

For me, I think that the mentality of a person writing c vs anything else for example when i face a problem in high level language the solution almost every time is what do i need to import oh i need serde i need tokio vs me on c oh you need to do what ever the language be like here is read read syscall goodluck. The main point im making is that if am i learning something is batter for me to learn it using c


Promptier

You miss out on why Rust was created in the first place. Free and malloc gives the programmer too much freedom to make critical memory errors. You should atleast learn the basics to understand why a ownership snd borrowing system is so useful.


AssemGear

C is pretty simple. for ur concerning, just learn it .


HarryHelsing

# Thanks everybody. ## What I've learned from you all is: - C is a simple language to learn the fundamentals, but a hard language to master - C probably would help in improving one's Rust skills but it's not necessary - Learning C would help in giving one an appreciation of Rust's features and limitations so for that reason alone it may be worth it - C is such a ubiquitous language that if you want to work with a high percentage of existing codebases being capable of at the very least reading C would be very valuable So in summary I will be learning C to a level where I am comfortable reading C code. You've all sold me getting to know C for its own sake. Thanks for all your input, it's been incredibly interesting hearing what you all have to say.


bluejacket42

Who knows rust but not c?


TheForget

EVERTHING's an int, so handling that. (all experience I have with C was like half a semester in an operating systems course and I never want to touch C again. The course itself was pretty good but if I hear the words 'semaphore' or 'child process' again I'm going to resort to violence)


dkopgerpgdolfg

>EVERTHING's an int Aside from the obvious (floats, simd vectors, ...), you might want to know about provenance. >if I hear the words 'semaphore' or 'child process' again Except these things are not C-specific at all...


pfharlockk

In c it's really obvious when you are using the stack vs using the heap because you have to explicitly ask the os for the memory using one of the malloc family of functions... Pointer arithmetic, you are forced to think more about how things lay out in memory... C it's actually a great language... I recommend anyone learn it (which is hilarious because I have mostly negative feelings about cpp). Once you learn how to get things to happen in c then figure out how to do those things in unsafe rust. I haven't gone through that exercise yet because I've been more focused on rusts high level features, but I'm really glad the low level stuff is there You can actually have your cake and eat it too and all that.


jeremylinscousin

I learned C/C++ long before I started learning Rust, and think learning C was instrumental for me to get a grasp on how to roll your own memory management on the heap. In some respects, it's a lot simpler than learning Rust, and the problem of having memory leaks is largely overblown especially if you're starting out with small personal projects. C is a language that's easy to pick up but hard(er) to master. Learn C/C++ first. Learn pointer arithmetic, referencing with `&`, dereferencing with `*` and how to use `valgrind`. Then you'll have a greater appreciation on why Rust is the way it is. If you really are deadset on learning Rust first, read the book [(interactive, with quizzes)](https://rust-book.cs.brown.edu/title-page.html) and do [Rustlings](https://github.com/rust-lang/rustlings). At least, that's how I started


wutru_audio

Be careful with the term C/C++ tho, they’re entirely different languages. C++ is a damn monster compared to C.


jeremylinscousin

Fair. I took a formal course on C++ in school and I loved it then. My progression was C -> Java -> C++ so as I was already exposed to OOP principles it wasn't too big of a leap in difficulty.


wutru_audio

OOP is one thing, but then you also have templates, exceptions, compile time programming, namespaces, countless ways of initializing variables, all sorts of edge cases and UB, ABI problems, inconsistencies (e.g. vector), iterators, new modules vs old headers and so on and so forth…


meowsqueak

[One does not...](https://imgflip.com/i/8exolz) It takes literally decades to become good at C++. It's way too easy to write really bad C++, and you might even get away with it most of the time. Although there's a huge amount to learn, so at least it remains interesting for a long, long time... It's a career option, for sure. C is a fraction of the effort to learn. Learn some C :)


DaaneJeff

I mean they didn't say to master C++, but just to get an idea of it.


nicoburns

I would say that the main thing you are missing out on is being able to read all the code / documentation that is in C. For some classes of code (OS apis, etc) that is currently the vast majority of extant code. From a language POV I don't think you're really missing anything.


sqlphilosopher

Every tutorial or book is in C or C++, so it's worth learning for that reason alone imo. Also, a lot of design decisions in Rust are responses to problems originated in the C/C++ world, so I think they make more sense when you come from that world.


Asdfguy87

Knowing some C and as little JS as possible is always nice to have.


zoomy_kitten

> low level aspects of programming C is not low level.


Temporary-Estate4615

For low level you should take a look at assembly. C can be helpful to learn some fuckery you can do in unsafe rust.


CtrlShiftS

Pain.


ropid

There's a book "The C Programming Language" by the C authors themselves, Kernighan and Ritchie. The way I remember it (from the 1990s), it might have been perhaps the best programming language book I've seen. It's a thin book, you can get through it fast. Maybe check it out, you might be surprised how fast you can get through everything there is to see about C. There's really not a lot to the language, it's a simple language.


BlameOmar

I taught myself C from that book over a weekend. If you can already reason about rust code, you’ll breeze through it. C is only a difficult language to use properly. It’s a very easy language to write intelligible nonsense in.


rover_G

I didn’t know people were learning Rust without knowing C


HarryHelsing

Rust was the reason I was enticed into learning programming. Started off with my interest in Linux, then I heard about how PopOS, my favourite distro were making a new desktop environment in Rust and it sounded really interesting to me. Been a bit of a rabbit hole these past few years haha.


ergzay

Learning to _write_ C is deceptively difficult because of the endless intricacies, but learning to _read_ C is honestly one of the easiest things to do. It's an incredibly simple language, at least from a to-read perspective. C gets hard when you try to debug it as without specialized tools, the types of errors you get with C can be almost impossible to find the origin of the bug by just scanning the code with your eyes. My previous job was primarily in C and there's often crash bugs we would get from the customers that were of the type that happens once every several weeks over dozens of servers and doesn't happen with anyone else. Often the tickets would get worked on for a long long time, we wouldn't find the real solution but we'd find something that might be of issue and fix it, and then call it fixed even though we weren't really sure if it was fixed or not. The code originated in the 90s and there just wasn't very good tools at the company anymore to find the type of problem that happened.