In the Rust bindings when using existing GObjects, there are no proxy objects or similar like in many other bindings. Instead they’re used as is and integrated with the Rust memory management mechanisms (RAII-style like in C++ for example): a glib::Object in Rust is literally just a pointer like in C (and the same one) and via the Rust Clone (mapping to g_object_ref()) and Drop (mapping to g_object_unref()) traits the memory is handled deterministically and the same as for any other Rust code. If you look at the assembly that is generated for Rust GObject code, it’s very close to what the corresponding C GObject code would look like. This also has the big advantage that it composes well with other languages used in the same process, while toggle references easily make it impossible to have more than one language in the process using them.
For actually creating GObject subclasses, I wrote an very explicit overview of that here some years ago. It’s again basically the same thing you would do in C, there’s basically no runtime overhead compared to C and it looks all the same from a C point of view. That has the big advantage that you can easily write C-compatible GObject libraries in Rust and have them used from any other language with GObject-Introspection support.
Now all that is super verbose, so with a lot of Rust type system trickery and some usage of macros the whole thing looks much simpler nowadays but under the hood still compiles to the same assembly. You can find an example of the current code you’d write in Rust here and explained in great detail here in @julian 's great documentation.
How this would map to Nim, I don’t know. Whenever a GC is involved mapping the behaviour of that nicely to GObject becomes complex but I guess mapping to the Nim reference counting memory management strategies should be nicely possible without having to deal with toggle references. As long as Nim provides you with the necessary API for doing that.
For creating subclasses you’ll have to find a way to map the way how it’s done in C (or what I describe in my blog post above for Rust) to Nim language constructs. I’m sure it would be possible to just do exactly the same as in C but that would be very verbose and nobody would like to write code like that. Based on my blog post, someone did the same for Go for example and with some additional work on top it seems usable in the go-gst GStreamer bindings. The Python, JavaScript, C#, etc. bindings also manage it in a similar way.
The main challenge is to map the whole thing to the target language in a way that it doesn’t feel too alien, isn’t too verbose and developers would like to make use of it. Making it work the same way as in C is usually not very difficult but then people could as well directly write C so that’s kind of defeating the point of using bindings.