I think there are languages where most of the standard library is written in themselves, however with most things marked as native or external and imported from a low-level library or module. These things would usually be functions, not variables; though maybe variable declarations can help on describing memory layout, but still maybe dangerous, I suppose.
Suppose you have a class TextMatch that can be inherited, in Java-like terms (where a single class is inherited and multiple protocols (interfaces) implemented). I think that for this to be possible, a super() call would have to take a pre-allocated object and all of the subtype's fields would come after the size of the base memory of TextMatch. For example, given this Java:
class C1 {
long x;
}
class C2 extends C1 {
long y;
}
The base structures for these two types (not the garbage-collected counterpart) could be, in Rust:
#[repr(C)]
struct C1Base {
x: i64;
}
#[repr(C)]
struct C2Base {
c1: C1Base;
y: i64;
}
I guessed of using some decorator in each native non-final class to express its base memory size, but I find it a bit dangerous:
(After that example follows an explanation of memorySize.)
package;
[FFI(memorySize = n)]
public class TextMatch {
// fooProperty is defined from Rust
public native function get fooProperty(): FooType;
/* etc ... */
}
memorySize indicates the byte payload for TextMatch fields as implemented in Rust. It's not necessarily what will be allocated for the instance object: that depends on the subtype being directly constructed. thus, calling new TextMatch() would be similiar to malloc(memorySize) and a call to the constructor code, however malloc() would be a garbage-collected object.
Calling a new SubtypeTextMatch() would construct an object with more memory, where its fields follow the byte payload specified by memorySize.
That will require that, whenever I add or remove new fields to C1Base from Rust, then I've to update the FFI decorator too for that supertype C1.
I've thought of avoiding this by separating all fields of that supertype into another heap object, exactly like Box<C1Base> from Rust:
Box<C1Base>
One thing I thought of too is that I can separate the base into another heap object (a pointer), thus allowing the base to be a constant memory size.
So it'd look like:
#[repr(C)]
struct C2Base {
c1: usize;
y: i64;
}
It's worthy remembering that pointers are platform-dependent, so it's best to use u64 and cast it to usize as needed, I suppose.
That said, the payload wouldn't change if I take this approach for every supertype. So it's a possible solution in my opinion, but I still am not sure if I'm taking the good practices here, including storing pointer as u64.
FFIdecorator I showed above. It's fine for the compiler to automatically decide the layout as long as there's no native super class inherited. $\endgroup$listmethods, for example. $\endgroup$FFIdecorator? It’s not clear to me what it is intended to allow you to do. Is the idea that you declareTextMatchessentially like you’d declare an interface, and the actual implementation is defined elsewhere? Your question discusses inheritance, but it’s not clear to me why inheriting from a class would be useful if it doesn’t have fields and methods accessible from within the language, and those must also conform to some agreed-upon protocol, so I don’t see howmemorySizecould be enough. $\endgroup$