Remarks for non-Rust languages


1. Every type implements __str__() and __repr__() for debug printing

2. Simple (non-union) enum types implement__richcmp__():

align = LayoutAlignItems.Stretch if align == LayoutAlignItems.Stretch: print("ok!")

3. union enums have a @staticmethod constructor and a hidden .match() function that will return the enum tag as a string and the enum payload as a PyObject:

size = OptionLogicalSize.Some(LogicalSize(600, 800)) tag, payload = size.match() if tag == "Some": print("size is {}, {}".format(payload.width, payload.height)) elif tag == "None": print("no size available")

4. new() constructors are the default constructors:

dom = Dom(NodeType.Div) # not Dom.new(NodeType.Div)!

5. If no explicit new() constructors exist, the default constructor takes all arguments in the order as they are specified in the API:

# API: struct ColorU { r: u8, g: u8, b: u8 a: u8 } # therefore the arguments to the default constructor are passed in order: color = ColorU(/*r*/ 255, /*g*/ 255, /*b*/ 255, /*a*/ 255)

6. Whenever a RefAny parameter is required to call a function, you can use any PyObject as a replacement:

mydata = MyClass() # your custom data type # App::new() takes a RefAny as the first argument - pass in your custom data type here app = App(mydata, AppConfig(LayoutSolver.Default))

7. All functions that take a *Vec type accept a PyList and all *Option types are automatically converted to and from Pythons None value:

monitors = app.get_monitors() # returns MonitorVec = PyList[Monitor] print(str(monitors[0])) # returns OptionLogicalPosition = Optional[LogicalPosition] cursor_relative = callbackinfo.get_cursor_relative_to_viewport() if cursor_relative is not None: print(str(cursor_relative))

8. Due to a bug in the Python PyO3 bindings, you cannot modify a struct through a struct (see issue).

window = WindowCreateOptions(LayoutSolver.Default) window.state.flags.frame = WindowFrame.Maximized # does not work # workaround (yes, it's annoying): state = window.state.copy() flags = state.flags.copy() flags.frame = WindowFrame.Maximized state.flags = flags window.state = state


1. Functions are named "Az" + class name + "_" + function name in pascalCase. Modules / namespaces do not exist:

app::App::new() = AzApp_new()

2. Enums are named "Az" + enum name + "_" + variant name:

LayoutAlignItems::Stretch = AzLayoutAlignItems_Stretch

3. union enums have special compile-time macros that crate the correct union variant + tag at compile time:

AzStyleCursorValue cursor = AzStyleCursorValue_Exact(AzStyleCursor_Grab);

4. If a class is marked with has destructor, it has a corresponding _delete() function. The destructors automatically call the sub-destructors of all fields (i.e. you do not need to recurse and delete every field manually).

# App is marked as "has_destructor": AzApp app = AzApp_new(/* ... */); # constructor AzApp_delete(&app); # destructor # value of app is undefined here

5. All classes can be deep-copied via _deepCopy() - note that this might be very expensive for large objects.

AzWindowCreateOptions w = AzWindowCreateOptions_new(); AzWindowCreateOptions copy = AzWindowCreateOptions_copy(&w);

6. To emulate pattern matching in C, every union enum has a corresponding $union_matchRef$variant() and $union_matchMut$variant() function. Both take a pointer to the union and an (uninitialized) pointer to the output. If the union is of variant $variant, the output pointer will be initialized:

// create a union enum AzStyleCursorValue cursor = AzStyleCursorValue_Exact(AzStyleCursor_Grab); // destructure a union enum AzStyleCursor* result; if AzStyleCursorValue_matchRefExact(&cursor, &result) { printf("ok!\n"); // result is initialized here }
The difference between _matchRef() and _matchMut() is that takes a const * and _matchMut() takes a restrict * to the result. In the example, the lifetime of result is equal to the lifetime of cursor (since result simply points to the payload of the tagged cursor union).

7. Run-time type reflection for RefAny can implemented via the AZ_REFLECT macro:

typedef struct { int field; } MyStruct; void MyStruct_delete(MyStruct* restrict A) { } // destructor AZ_REFLECT(MyStruct, MyStruct_delete);

The AZ_REFLECT macro generates functions to upcast from your struct to a RefAny as well as to do a checked downcast from a RefAny to your custom data type:

// create a new RefAny AzRefAny object = MyStruct_upcast(MyStruct { .field = 5 }); // Creates an uninitialized borrow and downcast it // fails if "object" is already borrowed mutably // or if the "RefAny" is not of type "MyStruct" (type check at runtime) MyStructRef structref = MyStructRef_create(&object); if MyStruct_downcastRef(&object, &structref) { printf!("ok! - %d", structref->ptr.field); } // unlocks "object" to be borrowed mutably again MyStructRef_delete(&structref); // Creates a borrow that can MODIFY the contents of the RefAny // (mutable "* restrict" borrow instead of "const*" borrow) // // The object cannot be borrowed referentially and mutably at // the same time - this invariant is checked at runtime. MyStructRefMut structrefmut = MyStructRefMut_create(&object); if MyStruct_downcastRefMut(&object, &structrefmut) { // structrefmut can modify the contents of the RefAny structrefmut->ptr.field = 6; // prints "6", value is now changed, visible to all copies of "object" printf!("ok! - %d", structref->ptr.field); } MyStructRefMut_delete(&structrefmut); // decreases the refcount - if refcount is 0, destructor is called if !MyStructRefAny_delete(&object) { /* RefAny is not of type "MyStruct" */ }

8. All *Vec data types have a _empty() constructor macro (to create an empty vec at compile time). The AzString type has an extra _fromConstStr() macro to define compile-time strings:

AzScanCodeVec v = AzScanCodeVec_empty(); AzString s = AzString_fromConstStr("hello"); // evaluated at compile-time


1. Like in Python, default constructors of classes take the arguments in the order or the fields:

// API: struct ColorU { r: u8, g: u8, b: u8 a: u8 } // therefore the arguments to the default constructor are passed in order: auto color = ColorU(/*r*/ 255, /*g*/ 255, /*b*/ 255, /*a*/ 255);

2. Explicit constructors are static functions, enums use enum class (C++11):

auto window = WindowCreateOptions::default(LayoutSolver::Default);

3. All by-value arguments require std::move in order to prevent accidental copies:

auto window = WindowCreateOptions::default(LayoutSolver::Default); app.run(std::move(window));

4. In difference to C, constructing a RefAny does not require macros, instead generics are used:

class MyStruct { int field; public: MyStruct(int f) noexcept: field(f) { } } auto object = RefAny::new(std::move(MyStruct(5))); auto objectref = object.downcastRef<MyStruct>(); if (objectref): // objectref = std::optional<Ref<MyStruct>> std::cout << objectref->ptr.field << std::endl; objectref.delete(); // release reference auto objectrefmut = object.downcastRefMut<MyStruct>(); if (objectrefmut):// objectrefmut = std::optional<RefMut<MyStruct>> std::cout << objectrefmut->ptr.field << std::endl; objectrefmut.delete(); // release reference // "object" RefAny destructor automatically decreases refcount here

5. All *Vec types have a _fromStdVector() function that converts a std::vector into the given Vec type without copying the contents of the array. Additionally a _fromStdString function exists to convert a std::string into a String type. All strings are UTF-8 encoded.

auto scancodes = std::vector{0,1,2,3}; auto converted = ScanCodeVec::fromStdVector(std::move(scancodes)); auto converted_back = converted.intoStdVector();

6. Like in C, all union enums have special functions to emulate pattern matching:

// create a union enum - Exact() is a constexpr function auto cursor = StyleCursorValue::Exact(StyleCursor::Grab); // destructure a union enum if (auto result = cursor.matchRefExact()) { // result = const StyleCursor* std::cout << result << std::endl; } if (auto result = cursor.matchRefMutExact()) { // result = StyleCursor* restrict *result = StyleCursor::Default; }

Classes and functions