silk.core.reflect.
Compile-time descriptions of the fields of a struct or tuple type, for generic code that visits each field.
When to use#
Use Reflect to write one generic function that works on the fields of many types, such as a
serializer or a formatter. Use Reflect.fields with static for to visit each field, and
Reflect.borrowField to read the value of a field at runtime, or Reflect.borrowFieldMut
to change it.
Details#
Descriptors exist only during static evaluation. They have no runtime value, so reflection adds
no runtime cost. The compiler expands a static for over Reflect.fields into one copy of
the loop body for each field, in declaration order. In each copy, the field has its own type.
A descriptor shows only the fields that are visible from the module that calls the reflection operation. Private fields of a type from another module are not in the list.
Examples#
Add all fields of a struct#
import silk.core.reflect { Reflect }
struct Pair {
pub first: i32,
pub second: i32,
}
fn sum(owner: &Pair) -> i32 {
let mut total = 0
static for field in Reflect.fields<Pair>() {
total = total + Reflect.borrowField<Pair, i32>(owner, field).*
}
return total
}
pub fn main() -> i32 {
let pair = Pair { first: 20, second: 22 }
if sum(&pair) != 42 {
return 1
}
return 0
}Import as Reflect with import silk.core.reflect { Reflect }.
Public declarations: 11.
namedKind#
pub const namedKind: u8The Reflect.typeKind result for a declared struct with named fields.
positionalKind#
pub const positionalKind: u8The Reflect.typeKind result for a declared tuple type with positional fields.
anonymousNamedKind#
pub const anonymousNamedKind: u8The Reflect.typeKind result for an anonymous record with named fields.
anonymousPositionalKind#
pub const anonymousPositionalKind: u8The Reflect.typeKind result for an anonymous tuple with positional fields.
arrayKind#
pub const arrayKind: u8The Reflect.typeKind result for a fixed array, such as [i32; 3]. It has no fields.
otherKind#
pub const otherKind: u8The Reflect.typeKind result for every other type, such as i32, string, a slice, an
enum, or a structural union A | B. It has no fields.
unionKind#
pub const unionKind: u8The Reflect.typeKind result for a nominal union, such as union Shape { ... }. It has no
fields of its own; list its variants with Reflect.variants.
labeledFieldKind#
pub const labeledFieldKind: u8The Reflect.fieldKind result for a field with a name. Read the name with
Reflect.fieldLabel.
positionalFieldKind#
pub const positionalFieldKind: u8The Reflect.fieldKind result for a field with a position. Read the position with
Reflect.fieldOrdinal.
variantFieldKind#
pub const variantFieldKind: u8The Reflect.fieldKind result for a field of a union variant, from Reflect.variantFields.
It has both a name, read with Reflect.fieldLabel, and a position in its variant, read with
Reflect.fieldOrdinal.
Reflect#
pub struct ReflectThe owner type of the reflection operations. It has no fields and is never constructed.
Details#
Each operation except Reflect.borrowField and Reflect.borrowFieldMut is static. It
runs only during static evaluation, such as in a static for, a static if, or a static fn.
Implementation Reflect#
impl ReflectAssociated function typeOf#
pub static fn typeOf<Owner>() -> Intrinsic.Type<Owner>Returns the compile-time descriptor of the struct or tuple type Owner.
Details#
Give the descriptor to Reflect.typeKind to find the kind of type.
Associated function fields#
pub static fn fields<Owner>() -> Intrinsic.Fields<Owner>Returns the compile-time descriptors of the fields of Owner, in declaration order.
Details#
Use the result as the sequence of a static for. Each element has the type
Intrinsic.Field<Owner, Value>, where Value is the type of that field.
Gotchas#
The list contains only the pub fields, even when the caller is the module that declares
Owner, because visibility is judged from silk.core.reflect. Call Intrinsic.reflectFields<Owner>()
directly to list the fields that the calling module can see.
Associated function typeKind#
pub static fn typeKind<Owner>(descriptor: Intrinsic.Type<Owner>) -> u8Returns the kind of type that descriptor describes: namedKind, positionalKind,
anonymousNamedKind, anonymousPositionalKind, arrayKind, or otherKind. Every
type has a kind, so a generic function can branch on the kind of its type parameter.
Associated function fieldKind#
pub static fn fieldKind<Owner, Value>(field: Intrinsic.Field<Owner, Value>) -> u8Returns labeledFieldKind for a named field, positionalFieldKind for a positional
field, or variantFieldKind for a field of a union variant.
Associated function fieldLabel#
pub static fn fieldLabel<Owner, Value>(field: Intrinsic.Field<Owner, Value>) -> string<'static>Returns the name of a named field, as written in the type declaration.
Gotchas#
Use this function only for a field where Reflect.fieldKind is labeledFieldKind or
variantFieldKind.
Associated function fieldOrdinal#
pub static fn fieldOrdinal<Owner, Value>(field: Intrinsic.Field<Owner, Value>) -> usizeReturns the zero-based position of a positional field.
Gotchas#
Use this function only for a field where Reflect.fieldKind is positionalFieldKind or
variantFieldKind.
Associated function borrowField#
pub fn borrowField<Owner, Value>(owner: &Owner, static field: Intrinsic.Field<Owner, Value>) -> &ValueReturns a shared borrow of the field of owner that field describes.
Details#
field is a static argument, so the compiler selects the field during specialization. At
runtime this function is a plain field access. The borrow lives as long as the borrow of
owner.
Gotchas#
field must come from Reflect.fields for the same Owner type. This function cannot move
a field out of an owned value.
Associated function borrowFieldMut#
pub fn borrowFieldMut<Owner, Value>(owner: &mut Owner, static field: Intrinsic.Field<Owner, Value>) -> &mut ValueReturns a mutable borrow of the field of owner that field describes.
Details#
field is a static argument, so the compiler selects the field during specialization. At
runtime this function is a plain field access. The borrow lives as long as the mutable borrow
of owner, and owner cannot be used while the field borrow is live.
Examples#
import silk.core.reflect { Reflect }
struct Counters {
pub hits: i32,
pub misses: i32,
}
fn reset(counters: &mut Counters) -> () {
static for field in Reflect.fields<Counters>() {
let place = Reflect.borrowFieldMut<Counters, i32>(&mut counters.*, field)
place.* = 0
}
return ()
}
pub fn main() -> i32 {
let mut counters = Counters { hits: 4, misses: 2 }
reset(&mut counters)
return counters.hits + counters.misses
}Gotchas#
field must come from Reflect.fields for the same Owner type. owner must be a mutable
borrow; a shared borrow is refused.
Associated function variants#
pub static fn variants<Union>() -> Intrinsic.Variants<Union>Returns the compile-time descriptors of the variants of the nominal union Union, in
declaration order.
Details#
Use the result as the sequence of a static for. Each element has the type
Intrinsic.Variant<Union>. Give it to Reflect.variantLabel, Reflect.variantFields,
Reflect.makeVariant or Reflect.isVariant.
Examples#
import silk.core.option { Option }
import silk.core.reflect { Reflect }
union Command {
Status,
Clone { pub url: string, pub depth: u32 },
}
static fn fieldCount<U>(variant: Intrinsic.Variant<U>) -> usize {
let mut count: usize = 0
static for field in Reflect.variantFields(variant) { count = count + 1 }
return count
}
// Builds the variant of `U` named `word`, if it has no fields.
fn unitNamed<U>(word: string) -> Option<U> {
static for variant in Reflect.variants<U>() {
static if fieldCount(variant) == 0 {
if word == Reflect.variantLabel(variant) {
return Option<U>.some(Reflect.makeVariant(variant, ()))
}
}
}
return Option<U>.none()
}
pub fn main() -> i32 {
let found = unitNamed<Command>("Status")
return match move found {
Option<Command>.Some {value: Command.Status} => 0
_ => 1
}
}Associated function variantLabel#
pub static fn variantLabel<Union>(variant: Intrinsic.Variant<Union>) -> string<'static>Returns the name of a variant, as written in the union declaration.
Associated function variantFields#
pub static fn variantFields<Union>(variant: Intrinsic.Variant<Union>) -> Intrinsic.Fields<Union>Returns the compile-time descriptors of the fields of a variant, in declaration order.
Details#
Each element has the type Intrinsic.Field<Union, Value>, where Value is the type of that
field, and the kind variantFieldKind. A unit variant has none.
Gotchas#
The list contains only the pub fields, because visibility is judged from silk.core.reflect.
A field descriptor of a variant cannot be given to Reflect.borrowField, because the
union holds the field only while that variant is active.
Associated function makeVariant#
pub fn makeVariant<Union, Fields>(static variant: Intrinsic.Variant<Union>, fields: Fields) -> UnionReturns the variant that variant describes, built from fields: a tuple of the variant's
fields in declaration order, or () for a variant without fields.
Details#
variant is a static argument, so the compiler selects the variant during specialization.
At runtime this function is a plain variant construction, such as
Shape.Line {length: a, width: b} for makeVariant(line, (a, b)). The tuple is
moved into the variant.
Examples#
import silk.core.reflect { Reflect }
union Shape {
Dot,
Circle { pub radius: u32 },
Line { pub length: u32, pub width: u32 },
}
fn area(shape: Shape) -> u32 {
return match move shape {
Shape.Dot => 0
Shape.Circle {radius} => radius * radius * 3
Shape.Line {length, width} => length * width
}
}
static fn fieldCount<U>(variant: Intrinsic.Variant<U>) -> usize {
let mut count: usize = 0
static for field in Reflect.variantFields(variant) { count = count + 1 }
return count
}
pub fn main() -> i32 {
let two: u32 = 2
let three: u32 = 3
let four: u32 = 4
let mut total: u32 = 0
static for variant in Reflect.variants<Shape>() {
let static count = fieldCount(variant)
static if count == 0 { total = total + area(Reflect.makeVariant(variant, ())) }
static if count == 1 { total = total + area(Reflect.makeVariant(variant, (two,))) }
static if count == 2 { total = total + area(Reflect.makeVariant(variant, (three, four))) }
}
// 0 + 12 + 12
if total != 24 {
return 1
}
return 0
}Gotchas#
The tuple must have exactly the variant's field types in declaration order. Every field of the
variant must be pub, since the variant is built from silk.core.reflect.
Associated function isVariant#
pub fn isVariant<Union>(owner: &Union, static variant: Intrinsic.Variant<Union>) -> boolReturns whether owner holds the variant that variant describes.
Details#
variant is a static argument, so at runtime this function is a plain test of the union's
active variant, like a match with one arm for that variant.
Category: Core.
Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2.