I've been in trouble when using tracing (or mark-n-sweep) garbage collector libraries, mainly because I tried to inherit a trait
. In practice previously, with a symbol table for my own language's compiler, I got a suggestion in using Weak<RefCell<T>>
(and keep a strong
Rc
refererence in a symbol pool), however this way I can't have two or more references to the same resource in the same scope. Another thing I did before this is use the gc
crate, however Gc
cannot be used as self
parameter.
Another possibility is to use unsafe
code, use struct Symbol
instead of trait Symbol
and a [repr(C)]
attribute, but even with unsafe
the code gets weird.
Possible solutions
a) Proper reference types (struct(ref)
) and struct(ref)
subtypes
There's a Rust dialect that supports a struct(gc)
annotation to indicate the struct
is by-reference. And WebAssembly supports reference types. So why not do the same and support inheritance together, which is a benefit for compiler developers, among other purposes?
Instead of struct(gc)
, it could be struct(ref)
, since ref
is an existing keyword and gc
would need to be a context keyword.
How it'd look like:
struct(ref) Symbol {
}
impl Symbol {
fn method_that_takes_self(&self) {
}
}
struct(ref) StringConstant: Symbol {
}
impl StringConstant {
#[override]
fn method_that_takes_self(&self) {
}
}
// covariant conversion
let s: &Symbol = StringConstant { ... };
// contravariant conversion
let c = s as StringConstant;
let c = s.try_into::<StringConstant>();
It'd implement:
-
std::clone::Clone
(no way to override) std::marker::Copy
std::convert::TryInto
- Field inheritance
- Method inheritance and overriding
- Instance-of test (do know what syntax or method could fit)
b) Smart pointer as self
parameter
There's also another way instead of the feature a): supporting a Gc
-like smart pointer as a self
parameter (and thus you use a trait
). But I believe proper reference types are more efficient, because for a trait to be used, several default methods are reimplemented for different types (like is_string_constant
to determine whether a Symbol
is a StringConstant
object), for example:
trait Symbol {
fn is_string_constant(&self) -> bool { false }
fn method_that_takes_self(self: &Gc<Self>) {
}
}
struct StringConstant {
}
impl Symbol for StringConstant {
fn is_string_constant(&self) -> bool { true }
fn method_that_takes_self(self: &Gc<Self>) {
}
}