Taking a hint from Cyclone, Rust etc one could consider managed pointers / objects. There are several possibilities:
1. Introduce something akin to move/borrow syntax with a special pointer type, eg. Foo@ x vs Foo* y and make the code track Foo@ to have unique ownership.
2. Introduce ref-counted objects with ref-counted pointers. Again use Foo@ x vs Foo* y with the latter being unretained. This should be internal refcounting to avoid any of the issues going from retained -> unretained that shared_ptr has. Consequently any struct that is RC:ed needs to be explicitly declared as such.
3. Managed pointers: you alloc and the pointer gets a unique address that will always be invalid after use. Any overflows will be detected, but use of managed pointers is slower due to redirect and check.
Sample code for (2)
type Foo struct @(refcounted) {
i32 a;
}
func Bar(Foo@ a)
{
printf("%d\n", sizeof(Foo)); // prints 8 due to RC
printf("%d\n", rc(a)); // prints 1
Foo@ x = a;
printf("%d\n", rc(a)); // prints 2
x = nil;
printf("%d\n", rc(a)); // prints 1
Foo* y = a;
printf("%d\n", rc(a)); // prints 1
Foo* z = malloc(sizeof(Foo)); // Not recommended!
// TOOD discuss how to properly initialize a RC:ed variable.
}