Skip to content

Instantly share code, notes, and snippets.

@diegopacheco
Last active August 11, 2025 19:59
Show Gist options
  • Save diegopacheco/d7db9088f46acba8892fa96cec4d5e36 to your computer and use it in GitHub Desktop.
Save diegopacheco/d7db9088f46acba8892fa96cec4d5e36 to your computer and use it in GitHub Desktop.
C3 Lang Tiny Essay

C3 Lang Tiny Essay

created: 16.MAR.2025

I love to learn new programing languages, it help to open the mind to new possibilities and compare different approaches. For instance, I learned Ruby and Scala in 2010, Clojure and Haskell in 2011, Go in 2015, Kotlin 2016, Rust in 2018 and Idris, TypeScript in 2019, 2020 Pandemic strike did a bunch of pocs but not with new langs(crazy year), Zig in 2021, 2022(coding in lots of langs but nothing new) - in 2023 I'm learning Nim and V. Learn at least one lang per year. This post is not complain, it's just to share some toughts, notes and impressions.

Why C3

  • Created in 2021 by Christoffer Lernö
  • Evolution not Revolution
  • Based on C / Full C ABI Compatibility
  • Generics
  • Safe Arrays
  • Zero Overhead Errors
  • Slices
  • Runtime and Compile Reflection
  • LLVM backend for industrial strength optimisations.

My Feelings (4-Aug-2024 c3 0.6.2 )

  • Fun
  • Fast
  • Nice C-Based Lang
  • Easier than Zig and Rust
  • More simple than Odin
  • Syntax is very Go-like
  • I feel C3 as a mix of Go and Zig.

Show me the code

My POCs with C3: https://github.com/diegopacheco/c3-playground

1 - Foreach

Like Go, Zig, Rust, Java, Python and many other languages we can easily do a foreach. Forearch can be with or wihout index.

import std::io;

fn int main(String[] args){
	
	int[5] arr = {1, 2, 3, 4, 5};
	foreach(int i : arr){
		io::printfn("%d",i);
	}

	foreach (int idx, int* &v : arr){
		io::printfn("index: %d value: %d",idx,*v);
	}

	return 0;
}

2 - Structs and Functions

C3 has structs, you can add functions to structs. Liek you would do in C++ or Zig.

struct Config{
    int port;
}

fn void Config.dump(Config* this){
    io::printfn("Port: %d",this.port);
}

fn int main(String[] args){
	Config config = {8080};
	config.dump();
	return 0;
}

3 - Catch If

It's like a try/cacth except that just catch errors and you can assign to variables. This is clean and useful, so you can do in a if-style like c, zig, go error handling similar styles.

int! result = goodLuck();
if (catch anyfault error = result){
  io::printfn("Error: %s",error);
}

It's also possible to do the opposite and we can do the If based on the try.

fn int main(String[] args){
	
	int! x = maybe();
	if (try int result = x){
		io::printfn("%d",result);
	}else{
		io::printfn("No value");
	}

	return 0;
}

fn int! maybe(){
	return 42;
}

4 - Compile time reflections

Besides the macro here, this is pretty cool. Being able to run reflections in compile time is great. Zig has something similar(IMHO zig is better).

struct Person{
    long id;
    int age ;
    String name;
}

macro print_fields($Type){
    $foreach ($field : $Type.membersof)
        io::printfn("Field %s, offset: %s, size: %s, type: %s",
                $field.nameof, $field.offsetof, $field.sizeof, $field.typeid.nameof);
    $endforeach
}


fn void main(){
    print_fields(Person);
}

5 - Generics

Like Java, Kotlin, Scala, Rust, Zig(with comptime) we can do generics in C3. Here is a generic stack implementation using generics in C3.

module stack(<Type>);

import std::io;

struct Stack{
    usz capacity;
    usz size;
    Type* elems;
}

fn void Stack.push(Stack* this, Type element){
    if (this.capacity == this.size){
        this.capacity *= 2;
        io::printfn("resizing");
        this.elems = realloc(this.elems, Type.sizeof * this.capacity);
    }
    this.elems[this.size++] = element;
}

fn Type Stack.pop(Stack* this){
    assert(this.size > 0);
    return this.elems[--this.size];
}

fn bool Stack.empty(Stack* this){
    return !this.size;
}

6 - Slices

Like Go, Zig and Rust, we can have slices in C3 lang.

fn int main(String[] args){
	
	int[5] a = { 1, 20, 50, 100, 200 };
	int[] b = a[0..4]; // The whole array as a slice.
	printArray(b,5);

	int[] c = a[0:5];  // The whole array as a slice.
	printArray(c,5);

	int[] d = a[2..3];  // 50, 100
	printArray(d,2);

	return 0;
}

fn void printArray(int[] a,int size) {
	for (int i = 0; i < size; i++) {
		io::printfn("%d",a[i]);
	}
}

7 - Contracts

C3 has support for contarcts, which is Design-By-Contracts(DBC) some languages hava frameworks/libs for DBC like Java.

fn int main(String[] args){
	int[] arr = {1, 2, 3};
	getLastElement(arr, 3);
	
	testFoo(-1);
	return 0;
}

/**
 * @param foo "the number of foos"
 * @require foo > 0, foo < 1000
 * @return "number of foos x 10"
 * @ensure return < 10000, return > 0
 **/
fn int testFoo(int foo){
    return foo * 10;
}

/**
 * @param array "the array to test"
 * @param length "length of the array"
 * @require length > 0
 **/
fn int getLastElement(int* array, int length){
    return array[length - 1];
}

Other Tiny Essays

About me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment