7 ms·
In cake object and memory are two resources. We can for instance, delete the object and reuse the same memory. For instance, this code is correct. #includ
by thradams 3y ago
In cake object and memory are two resources. We can for instance, delete the object and reuse the same memory.
For instance, this code is correct.
#include <ownership.h>
#include <stdlib.h>
struct X {
char * owner text;
};
void x_delete(struct X * owner p)
{
if (p)
{
free(p->text);
free(p);
}
}
int main() {
struct X * owner p = malloc(sizeof(struct X));
p->text = malloc(10);
free(p->text); //object text destroyed
struct X x2 = {0};
*p = x2; //x2 MOVED TO *p
x_delete(p);
//no need to destroy x2
}
- Arnavion 3y agolet p: Box<X> = Box::new(X { ... }); let x2 = X { ... }; // Moves x2 into the same memory as the first X. // The first X is automatically dropped as part of this assignment. // Also consumes x2 so x2 is not available any more. *p = x2; // Drops the X that was originally assigned to x2 and then moved into p. drop(p); // No need, nor is it possible, to destroy x2.
- masklinn 3y agoAnd as bonus there is no temporal hole where you could access a null or dangling text. Here it is as a runnable snippet: https://godbolt.org/z/fc4Gfxrfd https://godbolt.org/z/fc4Gfxrfd
- thradams 3y agoIn cake there is no temporal hole, we cannot reuse the deleted object. This prevents double free and use after free. int main() { struct X * owner p = malloc(sizeof(struct X)); p->text = malloc(10); free(p->text); //object text destroyed //p->text is on uninitialized state. //cannot be used (except assignment) struct X x2 = {0}; *p = x2; //x2 MOVED TO *p x_delete(p);
- thradams 3y agoThanks for the rust sample. It looks very similar. Can the allocator be customized? As I said I am not Rust specialist. Also, in my understanding is that in Rust, sometimes a dynamic state is created when the object may or may not be moved. In cake ownership this needs to me explicit ( and the destructor is not generated) I also had a look at Rust in lifetime annotations. This concept may be necessary but I am avoiding it. Consider this sample. struct X { struct Y * pY; }; struct Y { char * owner name; }; An object Y pointed by pY must live longer than object X. (Cake is not checking this scenario yet) Also (classic Rust sample) int * max(int * p1, int * p2) { return *p1 > *p2 ? p1 : p2; } int main(){ int * p = NULL; int a = 1; { int b = 2; p = max(&a, &b); } printf("%d", *p); } This is not implemented yet but I want to make the lifetime of p be the smallest scope. (this is to avoid lifetime annotations) int * p = NULL; int a = 1; { int b = 2; p = max(&a, &b); } //p cannot be used beyond this point*
- masklinn 3y ago> Also, in my understanding is that in Rust, sometimes a dynamic state is created when the object may or may not be moved. Yes? if cond { drop(p) } // p may or may not be dropped here or let p; if cond { p = something(); } // p may or may not be set here These trigger dynamic drop semantics, in which case the stackframe has a hidden set of drop flags going alongside any variable with dynamic drop semantics, to know if they do or don’t need to be dropped. The flags are automatically updated when the corresponding variables are set or moved-from.
- thradams 3y agoThis "dynamic drop semantics" does not exist in cake. int * owner p = malloc(sizeof(int)); if (condition) free(p); free(p); // error p may be initialized/moved. to fix int * owner p = malloc(sizeof(int)); if (condition) { free(p); p = 0; } free(p);
- Arnavion 3y ago>Can the allocator be customized? `Box<T>` is the type of an owning pointer that uses the default global allocator, and `Box<T, A>` is the type of an owning pointer that uses an allocator of type `A`. The latter is unstable, ie it can only be used in nightly Rust. (Also the fact that the latter changes the type means a large part of existing third-party code as well as a bunch of code in libstd itself becomes unusable if you want to use a type-level custom allocator because they only work with `Box<T>`. But that's a different discussion...) >An object Y pointed by pY must live longer than object X. Yes, the py field in Rust would use a reference type instead of a pointer, and the reference would need to have a lifetime annotation, and the compiler would work to prevent the situation you describe: struct X<'a> { py: &'a Y } let y = Y { ... }; let x = X { py: &y }; drop(y); // error: y is borrowed by x so it cannot be moved. But to be clear, the `'a` lifetime syntax is not what's making this work. What's making this work is that the compiler tracks the lifetimes of references in general. This works in the same way even though there are no lifetime annotations: let y: String = "a".to_owned(); let x = &y; drop(y); // error: y is borrowed by x so it cannot be moved. do_something_with(x); The explicit lifetime annotations are just for a) readability, and b) because sometimes you want to name them to be able to express relationships between them. Eg if two lifetimes 'a and 'b are in play and you want to express that 'a is at least as long as 'b, then you have to write a `'a: 'b` bound. In many cases they can be omitted and the compiler infers them automatically.