Live data from Hacker News

Native Reflection in Rust

jack.wrenn.fyi

51–60 of 68 posts

Re: Native Reflection in Rust

#52
post #49

Earlier quoted context omitted.

Absolutely! That's the approach that frunk [0] takes. Frunk (and other reflection libraries like it) are suitable for most use cases, and make better use of Rust's affordances. My crate is suitable for cases where you cannot know (or control) the set of types you might need to reflect on in advance. It's primary use-cases are related to debugging. [0]: https://docs.rs/frunk

Is Frunk Rust's Shapeless (from Scala)?

Yep!

Re: Native Reflection in Rust

#53
post #33

Earlier quoted context omitted.

That's runtime reflection. Compile time reflection AFAIK is available in D and Zig, and is planned for C++.

That's right. Nim does as well. It's amazing. Once you get used to having CTTI and being able to use it, it's hard to program without it. Bonus points if you can do basic dependent types too. In C++ with SFINAE you can effectively do CTTI-style programming in C++. C++ has long had runtime type reflection as well (RTTI), though it needs to be compiled in. Looks like there's a boost library for CTTI.

The C++ reflection improves a lot in C++20, but it's still very limited compared to that aspect of Nim, or even Zig. The std::meta::info and "splices" based on Haskell for C++26 are incredibly exciting to me. I have many use cases in mind. Splices in combination with std::embed will make C++ basically just a bad Racket (but one with inline assembly!).

Re: Native Reflection in Rust

#55

Does this still work if the application is complied in release mode or with optimizations? Even if not, this is still very useful for debugging

It only works if DWARF is generated. By default, the `release` profile of Cargo sets `debug = false` [0]. But, it's quite easy to override this setting, and have a build that is both optimized and includes debuginfo.

[0]: https://doc.rust-lang.org/cargo/reference/profiles.html#rele...

Re: Native Reflection in Rust

#56
Great writeup! The defmt logging crate uses a linker script to extract debug symbols so that you get nicely formatted stack traces on embedded systems. It works on linux, macos and windows. I wonder if the same technique can be applied to this project. It needs a runner though so may not be the right approach.

https://github.com/knurling-rs/defmt

Re: Native Reflection in Rust

#57
post #41

My version of Greenspun's Tenth [1] is that any sufficiently complex static language contains an adhoc, informally specified, bug ridden and slow version of a dynamic "any" type. Thx OP for providing an example. [1] https://en.wikipedia.org/wiki/Greenspun's_tenth_rule

Rust has a dynamic any type, `std::any::Any`.

Re: Native Reflection in Rust

#58
post #9

Earlier quoted context omitted.

It's a fancy way of saying "every time this type is used, replace all the generic type params with what was used and generate code for it". It's how generics are implemented in Rust. If you have struct Foo (T); And you create Foo(42i32) and Foo(0.0f64), the compiler will create the equivalent to struct Fooi32(i32); struct Foof64(f64); In other languages like Java, generics are implemented the way that Rust does "trai…

Nice examples - you can also have languages (like SML) where monomorphization is simply an implementation detail. Some compilers (e.g., MLton) perform monomorphization and others don't.

That depends on what you mean. SML has "polymorphism" boiling down to being able to plug an arbitrary type in some places, which is denoted like 'a. But when people talk about generics, they more often talk about C++ templates, Java generics, Rust traits, etc. whose SML equivalent are signatures, structs and functors. Signatures are a bit like Rust traits, structs are a bit like Rust implementations of traits, whereas functors are like Rust's "templates", i.e. wherever you swap angle brackets to parametrise something with types constrained by traits, or values constrained by types. Except in Rust this parametrisation can be slapped on a bunch of things. It can be on structs, on functions, on traits, on implementations of traits etc. In SML you need to group all the "parametrised" things into a struct (and a corresponding signature), which is going to be returned by a functor.

And now the thing is: with transparent signature ascriptions, functors are monomorphised in SML, instead of everything being hidden behind signatures (as is in the case of Rust with traits when you use dyn), which has semantic consequences. E.g. a struct returned by a functor may contain a type. You can't perform proper type-checking without monomorphising, because you don't know what the exact type is. E.g. in the following program, the final line couldn't be type-checked without monomorphisation:

   signature ITERABLE = sig
       type ElemT
       type SrcT
   
       val new_iter: SrcT -> unit -> ElemT option
   end
   
   signature LIST_ELEM_TYPE = sig
       type T
   end
   
   functor ListIterFun (ListElemType: LIST_ELEM_TYPE): ITERABLE = struct
       type ElemT = ListElemType.T
       type SrcT = ElemT list
   
       fun new_iter l = let val lr = ref l
                        in
                          fn () => case !lr of
                                     nil => NONE
                                   | (x::xs) => (lr := xs; SOME x)
                        end
   
   end
   
   structure IntElemType: LIST_ELEM_TYPE = struct
       type T = int
   end
   
   structure IntListIter = ListIterFun(IntElemType)
   
   val next = IntListIter.new_iter [1, 2, 3, 4, 5]
If I change the signature ascription on ListIterFun to an opaque ascription (:> ITERABLE), the final line won't type-check, because it's not obvious from the signature, that ElemT is int. So transparent signature ascriptions require monomorphisation (Rust traits without dyn), and opaque signature ascriptions free the compiler from having to do monomorphisation (Rust traits with dyn*).

There was a lot of discussion of this issue when Go was settling on a design for its generics, under the phrase "reified generics".

Re: Native Reflection in Rust

#59
post #32

Earlier quoted context omitted.

> This is a rather strange thing to bolt onto a language. It can just be an extremely fun and cute demo, without practical application.

how about adding this to debuggers for better object-views? (could it be possible to provide near-js/python/java level of obj view?)

DWARF is a standard for data to support debuggers, so this crate does effectively the opposite: it uses info normally only available during debugging to provide reflection.

Re: Native Reflection in Rust

#60
post #36

I've used very similar method, at work, to provide C++ "reflection" between my own system and a system from another team. Basically, the other system is a dynamic library which sends and receives C structures from my application. Those structures are then mapped into a buffer that is supposed to have the same size and there are pointers with metadata pointing into the buffer that are supposed to be exactly like the s…

Was the other team completely unwilling to provide a header?
Post reply on HN