<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/c-for-rust-programmers-labu1lacg" -->

---
title: C for Rust Programmers | daily.dev
description: A Rust developer's exploration of C for the first time, cataloging surprising language quirks: C99 added a stdbool.h header for booleans that merely expand to...
canonical: https://daily.dev/posts/c-for-rust-programmers-labu1lacg
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: C for Rust Programmers | daily.dev
og:description: A Rust developer's exploration of C for the first time, cataloging surprising language quirks: C99 added a stdbool.h header for booleans that merely expand to...
og:url: https://daily.dev/posts/c-for-rust-programmers-labu1lacg
og:image: https://api.daily.dev/og/posts/laBu1lAcg.png
og:image:alt: C for Rust Programmers
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# C for Rust Programmers

**[Lobsters](https://daily.dev/sources/lobsters)** · 11 min read · 2 upvotes · 1 comments

## Summary

A Rust developer's exploration of C for the first time, cataloging surprising language quirks: C99 added a stdbool.h header for booleans that merely expand to 1 and 0 (true primitives arriving only in C23); null-terminated strings require manual allocation of an extra byte and tracking of terminators, unlike Rust's length-prefixed &str; integer widths like long vary by platform (64-bit on Unix, 32-bit on Windows), so fixed-width types from stdint.h are recommended; C's error handling relies on magic return values and errno rather than Result types; field access uses '.' for values and '->' for pointers; arrays decay into pointers when passed to functions, silently changing what sizeof() reports; and C has no borrow checker or references, only raw pointers with manual arithmetic.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://bd103.dev/blog/2026-10-07-c-for-rust-programmers>

## Questions this post answers

### why does sizeof() return 8 bytes for an array parameter inside a C function instead of its declared size

Arrays passed as function parameters in C are implicitly converted to pointers to their first element, so sizeof() reports the pointer size (8 bytes on 64-bit systems) rather than the array's actual byte length. This happens even when the parameter is declared with an explicit size like char arr[3]. Clang warns about this with the -Wsizeof-array-argument flag to help catch the mistake.

_daily.dev surfaces explainers like this for developers debugging confusing sizeof behavior in C._

### why does C need a null terminator for strings instead of storing the length like Rust's str

C strings are terminated with a null byte (\0) instead of storing a separate length value, which means every string needs extra memory for that terminator and even empty strings take up one byte. This trade-off avoids needing an extra size variable but makes functions like strlen() require O(n) time and makes embedding null bytes mid-string unsafe. Rust's &str instead uses a 16-byte fat pointer storing both address and length.

_developers weighing C's manual string handling against Rust's safer abstractions can track these trade-offs on daily.dev._

### why is the C long type 64 bits on Unix but only 32 bits on Windows

C only guarantees long is at least 32 bits, leaving the exact width up to the platform; on 64-bit Unix systems long is 64 bits, while on 64-bit Windows it remains 32 bits. To avoid this platform-dependent inconsistency, fixed-width integer types from stdint.h, such as int32_t and uint64_t, are recommended for cross-platform code instead of relying on char, short, int, or long.

_daily.dev helps developers writing cross-platform C code keep track of platform-specific integer sizing gotchas._

## Community take

How the wider developer community reacted, aggregated from 2 discussions and 14 comments across lobsters, hackernews (as of 2026-10-08).

**TL;DR:** The discussion largely agrees the article's observations about C are accurate but historically explainable, with side conversations about garbage collectors for C and the nuances of what counts as 'safety'.

**Sentiment:** 40% positive · 45% mixed · 15% skeptical

**The case for**

- C's quirks are acknowledged as true but rooted in legitimate historical constraints rather than poor design.
- It's noted that modern tools make it feasible to bolt a garbage collector onto C applications.
- One commenter highlights newer compilers now accept true/false without requiring stdbool.h.

**The pushback**

- Claims that alternatives like FilC offer better safety than Rust are pushed back on, since Rust's compiler can reject unsound patterns that C simply can't express enough information to catch.
- A tangent notes that even straightforward-looking string/codepoint handling in Rust can be non-idiomatic or break for scripts with combining characters.

**By community**

- lobsters (mixed): Mostly agreeable and expansive discussion, with one pointed disagreement over whether C-based safety tools can really match Rust's guarantees.
- hackernews (mixed): Very light engagement, with brief tangents about struct naming conventions and a related C-to-Rust transpiler project.

**Hottest debate:** Whether tools like FilC or garbage collectors can give C comparable or better safety than Rust, versus the view that C fundamentally lacks the information needed to guarantee it.

**Open questions**

- What does modern application-level (non-embedded) C development actually look like today with garbage collection options?

**Highlights**

> > The one thing I'd say is it's easy to hook up a performant garbage collector in most cases... or just use FilC to compile, which gives you better safety than you'd get in Rust. Only if you define "safety" in certain ways. There's no way to magically fix certain cases where Rust is safer because the C source code simply doesn't give enough information to the compiler/runtime to know whether something is intentional or a bug, while the Rust standard library was designed to refuse to compile those cases. (C and C++ lean in the direction of "trust the programmer" while Rust leans in the direction of "if it can't be proven correct, assume it's incorrect and reject it".)
> — [ssokolow on lobsters · 2 points](https://lobste.rs/s/lgdxtw/c_for_rust_programmers#c_sq41ex)

> All of these things are true, but mainly with historical reasons.  They then cannot be undone because so much of the world is dependent on C code, so lots of things need to keep compiling. The one thing I'd say is it's easy to hook up a performant garbage collector in most cases... or just use FilC to compile, which gives you better safety than you'd get in Rust. I'm certainly not saying C's a better language; Rust has the benefit of being developed ~40 years later. It is not hard to make a better language than C. Still, Rust and Zig both deserve a lot of credit for being able to compete at all in C's niche-- they may eventually become as *important* as C is. And just a nit on your footnote: C23 in Clang accepts `true` and `false` without including <stdbool.h>, and has for quite a while.
> — [viega on lobsters · 1 points, 2 comments](https://lobste.rs/s/lgdxtw/c_for_rust_programmers#c_pyzv6p)

> > The corresponding Rust function is not idiomatic, and can only correctly handle ASCII text. If I were writing a production version of this function, it would be a single line of code Of note: because it works on codepoints the line in question only works for scripts without combining characters or those which have all the precomposed form you need *and* you have normalised the input. I believe that is a very common issue for Aramaic and Brahmic scripts, but even the first can occur in Latin scripts despite their favoured position: the guarani script uses the character g̃ which afaik has no precomposed form, it only exists as the sequence Latin Small Letter G, Combining Tilde.
> — [masklinn on lobsters · 1 points](https://lobste.rs/s/lgdxtw/c_for_rust_programmers#c_qtoyig)

> Yes, the Boehm collector has been around a long time. Simple garbage collectors are cheap easy to write, _especially_ if you are not using multiple threads. A few other things that've been around for a while: - https://libcello.org/learn/garbage-collection - https://github.com/mkirchner/gc - https://github.com/orangeduck/tgc
> — [viega on lobsters · 1 points](https://lobste.rs/s/lgdxtw/c_for_rust_programmers#c_rxzm0w)

**Source threads**

- [lobsters](https://lobste.rs/s/lgdxtw/c_for_rust_programmers) · 33 points · 11 comments
- [hackernews](https://news.ycombinator.com/item?id=49996048) · 2 points · 3 comments

## Community discussion

Top comments from developers on daily.dev.

**@pdfopsdev** · 0 upvotes

> the sizeof gotcha on decayed arrays gets people every time. pass a stack array into a function and sizeof(arr)/sizeof(arr[0]) silently returns 1 instead of the real length because you're sizing a pointer now.

## Similar posts on daily.dev

- [some C habits I employ for the modern day](https://daily.dev/posts/some-c-habits-i-employ-for-the-modern-day-xxclsjssp) · Lobsters · 2 upvotes · 0 comments
- [Rust VS C\+\+ Comparison for 2026](https://daily.dev/posts/rust-vs-c-comparison-for-2026-ud1vahdum) · JetBrains · 4 upvotes · 1 comments
- [C Programming Language Quiz](https://daily.dev/posts/c-programming-language-quiz-ddv6hxsro) · Lobsters · 0 upvotes · 0 comments
- [Rewriting the world in Rust — Bitfield Consulting](https://daily.dev/posts/rewriting-the-world-in-rust-bitfield-consulting-vxpqrwzmv) · Bitfield Consulting · 69 upvotes · 8 comments

---

Tags: [#rust](https://daily.dev/tags/rust), [#c](https://daily.dev/tags/c)

[View this post on daily.dev](https://daily.dev/posts/c-for-rust-programmers-labu1lacg)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"C for Rust Programmers","url":"https://daily.dev/posts/c-for-rust-programmers-labu1lacg","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/c-for-rust-programmers-labu1lacg"},"datePublished":"2026-10-07T21:42:53.839Z","dateModified":"2026-10-08T03:05:57.880Z","description":"A Rust developer's exploration of C for the first time, cataloging surprising language quirks: C99 added a stdbool.h header for booleans that merely expand to...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/8270a929e085b74ddffc8538ba111c56?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/8270a929e085b74ddffc8538ba111c56?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Lobsters","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Lobsters","logo":"https://media.daily.dev/image/upload/s--tl8v_Fku--/f_auto,t_logo/v1698841318/logos/lobste.jpg","url":"https://daily.dev/sources/lobsters"},"commentCount":1,"discussionUrl":"https://daily.dev/posts/c-for-rust-programmers-labu1lacg","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":2},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":1}],"keywords":"rust,c","timeRequired":"PT11M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Lobsters","item":"https://daily.dev/sources/lobsters"},{"@type":"ListItem","position":3,"name":"C for Rust Programmers"}]}
{"@context":"https://schema.org","@type":"WebPage","@id":"https://daily.dev/posts/c-for-rust-programmers-labu1lacg","comment":[{"@type":"Comment","text":"the sizeof gotcha on decayed arrays gets people every time. pass a stack array into a function and sizeof(arr)/sizeof(arr[0]) silently returns 1 instead of the real length because you’re sizing a pointer now.","datePublished":"2026-10-08T18:14:18.710Z","url":"https://daily.dev/posts/laBu1lAcg#c-2ZanT5WFO","author":{"@type":"Person","name":"PDFops","url":"https://daily.dev/pdfopsdev","image":"https://media.daily.dev/image/upload/s---8isRBKc--/f_auto/v1782922291/avatars/avatar_orjMeK8QKaaVZwGq7ScPz?_a=BAMAMicg0"}}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/c-for-rust-programmers-labu1lacg#faq","mainEntity":[{"@type":"Question","name":"why does sizeof() return 8 bytes for an array parameter inside a C function instead of its declared size","acceptedAnswer":{"@type":"Answer","text":"Arrays passed as function parameters in C are implicitly converted to pointers to their first element, so sizeof() reports the pointer size (8 bytes on 64-bit systems) rather than the array's actual byte length. This happens even when the parameter is declared with an explicit size like char arr[3]. Clang warns about this with the -Wsizeof-array-argument flag to help catch the mistake. daily.dev surfaces explainers like this for developers debugging confusing sizeof behavior in C."}},{"@type":"Question","name":"why does C need a null terminator for strings instead of storing the length like Rust's str","acceptedAnswer":{"@type":"Answer","text":"C strings are terminated with a null byte (\\0) instead of storing a separate length value, which means every string needs extra memory for that terminator and even empty strings take up one byte. This trade-off avoids needing an extra size variable but makes functions like strlen() require O(n) time and makes embedding null bytes mid-string unsafe. Rust's &str instead uses a 16-byte fat pointer storing both address and length. developers weighing C's manual string handling against Rust's safer abstractions can track these trade-offs on daily.dev."}},{"@type":"Question","name":"why is the C long type 64 bits on Unix but only 32 bits on Windows","acceptedAnswer":{"@type":"Answer","text":"C only guarantees long is at least 32 bits, leaving the exact width up to the platform; on 64-bit Unix systems long is 64 bits, while on 64-bit Windows it remains 32 bits. To avoid this platform-dependent inconsistency, fixed-width integer types from stdint.h, such as int32_t and uint64_t, are recommended for cross-platform code instead of relying on char, short, int, or long. daily.dev helps developers writing cross-platform C code keep track of platform-specific integer sizing gotchas."}}]}
```

