Rust

5 Posts

From Return Codes to Result

5 minute

C developers know this pattern well:

int read_config(const char *path, struct Config *out);

The function returns 0 on success and a non-zero value on failure; the real result is written through an output parameter. System calls often use -1 plus errno. With more resources, the code usually grows a goto cleanup path.

This works, but it has long-term costs:

  • callers may forget to check the return value
  • success values and error codes live in different places
  • output parameters need rules for failure cases
  • integer error codes often lose context
  • cleanup paths get tangled with error propagation

Rust Result is not an exception mechanism. It is closer to putting “success or failure” directly into the function return type.

Read More

Strings Are Not char Pointers

5 minute

C char * is powerful and vague.

It may mean mutable bytes, a NUL-terminated string, ASCII text, or simply the argument type required by a C API. The caller also has to know who frees the memory, whether it contains \0, what encoding it uses, and where the length comes from.

Rust does not put all of that into one type. It splits the common cases:

one byte: u8
bytes: &[u8] / Vec<u8>
UTF-8 text view: &str
owned UTF-8 text: String
C string view: &CStr
owned C string: CString

This article builds the first boundary: bytes, text, and C strings are different things.

Read More

Buffers, Slices, and Vec

6 minute

In C, a binary buffer is often represented as pointer plus length:

int parse_packet(const uint8_t *buf, size_t len);

This pattern is flexible, but it carries risk. Whether buf may be null, whether len is trustworthy, whether the function mutates data, whether it stores the pointer, and when the caller may free memory are all conventions outside the type.

Rust does not remove this pattern. It splits it into more precise types:

read-only view: &[u8]
writable view: &mut [u8]
owned buffer: Vec<u8>
fixed array: [u8; N]

This article is only about binary buffers. Strings, String, and &str come later.

Read More

Basic Types, Structs, and Memory Layout

3 minute

When C developers first look at Rust types, the obvious move is to find familiar mappings: uint8_t to u8, int32_t to i32, size_t to usize, struct to struct.

That is a good starting point, but similar names are not enough.

In C, basic types, struct layout, alignment, and ABI affect file formats, network packets, register maps, dynamic library interfaces, and cross-language calls. Rust also cares about these issues, but its defaults have a different goal: Rust types first serve internal safety and optimization. If a type has to match C or a binary layout, that boundary must be made explicit.

Read More

Start with main and Cargo

6 minute

For a C developer writing Rust for the first time, the unfamiliar part is often not if, for, or function calls. The first real difference is the engineering entry point: C programs often begin with main.c, gcc, Makefiles, and argc/argv; Rust programs usually begin inside a Cargo project.

That is not just a tooling difference. Cargo manages source layout, dependencies, builds, tests, and package metadata. main also becomes more than a function returning an integer: error handling, standard output, standard error, and exit status enter the program structure early.

Read More