~yerinalexey

On memory safety

October 29, 2021

One of the things that all Rust people bring up when discussing Rust vs. <any other language> is how Rust guarantees memory safety and the program will never run into 'Segmentation fault's or unpredicatable behavior. This is completely true! The only problem is that Rust (or C++ to some extent) has the monopoly on this feature.

I think it's important to have a viable competitor to Rust with similar level of memory safety, but *simpler*. Rust's compiler is extermely huge, complex and hard to bootstrap. This hurts both people porting it to new architectures and operating systems, and distribution packagers who don't want to rely on binary releases.

The core of this feature is quite simple - reference counting at compile-time. Each value that takes up memory (either on the stack or using heap allocation) have a maximum number of references at that time period.

For example, reference to an object is passed to a few functions: one returns immediately, but the second stores a reference somewhere. Then someone wants to free the object. This won't work because it still holds a reference!

struct Object {
	// Some fields
}

fn do_something(obj: *Object) {
	// Took an object, obj refcount += 1

	// ...

	// Done using it, obj refcount -= 1
}

let global_object: *Object;
fn do_something_else(obj: *Object) {
	// Took an object, refcount += 1

	// global_object also references it, refcount += 1
	global_object = obj;

	// Done using it, refcount -= 1
}

// '&' moves an object into the function
fn free_object(obj: &Object) {
	// Refcount is 1, which is the global_object variable
	// This should fail to compile and say that global_object still holds a reference
	free(obj);
}

fn main() {
	let obj = new_object(); // *Object, refcount 1 (this variable)

	do_something(obj); // refcount still 1 (returned)
	do_something_else(obj); // refcount is 2 because of global_object!

	// We moved, refcount -= 1
	free_object(obj);

	// obj is invalid from now on because free_object moved it
}

This is only a simple case to run through manually, and it could be a bit more convoluted when there are a lot of objects referencing each other and running this manually would be harder, so that's where a compiler would do this tracking instead. There are other cases like arrays of references, which are trickier, but not impossible to track.

This kind of refcounting also opens other possibilities of memory management like automatically inserting a free() when reference counter hits 0 (also known as RAII).

And to finish off, there should be some way to circumvent safety measures, because it may be necessary for interoperation with code in other languages or to make a valid but unsupported pattern work.

If you know a language like this which already exists, or you want to share your thoughts on memory safety, head over to ##yyp channel on libera.chat IRC network, I'll be happy to know.