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.
Native Reflection in Rust
21–30 of 68 posts
Re: Native Reflection in Rust
#22Earlier 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…
Pretty sure that some usage patterns of polymorphic types can not be completely monomorphized. Here's example in Golang: package main import ( "fmt" ) type wrapper[T any] struct { Value T } func (w wrapper[T]) String() string { return fmt.Sprintf("{%v}", w.Value) } func stringWrapped[T any](n int, v T) string { if n == 0 { return fmt.Sprintf("%v", v) } return stringWrapped(n-1, wrapper[T]{Value: v}) } func main() { n…
[1]: https://play.rust-lang.org/?version=stable&mode=debug&editio...
[2]: https://play.rust-lang.org/?version=stable&mode=debug&editio...
^ This blows the stack because it keeps calling itself with no break condition, but shows how the type system accepted the code.
Re: Native Reflection in Rust
#23Earlier 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…
I think cpp does this too
Given
fn foo(a: T, b: T) -> T { a + b }
The compiler will complain that you should have been explicit on how T is going to be used: error[E0369]: cannot add `T` to `T`
--> src/lib.rs:1:32
|
1 | fn foo(a: T, b: T) -> T { a + b }
| - ^ - T
| |
| T
|
help: consider restricting type parameter `T`
|
1 | fn foo>(a: T, b: T) -> T { a + b }
| +++++++++++++++++
whereas in C++ this would have been accepted until you called foo with two things that couldn't be added together, like a Rust macro[1].[1]: https://play.rust-lang.org/?version=nightly&mode=debug&editi...
Re: Native Reflection in Rust
#24Earlier quoted context omitted.
To add to this, even the Foo-wrapper is gone, just the i32 remains. Rust values are amorphous data blobs at runtime.
Yes, that's true but that is an implementation detail that only comes into play when dealing with ABI, and then you should be using #[repr(transparent)] to ensure that the compiler won't do something else :)
Re: Native Reflection in Rust
#25Earlier quoted context omitted.
What are monomorphic data types? What should be my first read on the subject?
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…
Re: Native Reflection in Rust
#26Earlier quoted context omitted.
Yes, that's true but that is an implementation detail that only comes into play when dealing with ABI, and then you should be using #[repr(transparent)] to ensure that the compiler won't do something else :)
Sure, it’s good to point out the difference between “the behavior of a typical optimizing compiler” and “things actually guaranteed by the language”. The context of the discussion was the former, I think. I’m not even that certain that monomorphization is actually required in theory.
pub fn foo>(x: T) {
inner_foo(x.as_ref());
}
fn inner_foo(_: &X) { todo!() }
can be instead done by the compiler automatically (turning monomorphized code back into polymorphic code, hence the polimorphization hame).[1]: https://rustc-dev-guide.rust-lang.org/backend/monomorph.html...
Re: Native Reflection in Rust
#27Today I learn that Rust does not have reflection.
Re: Native Reflection in Rust
#28Earlier quoted context omitted.
Except Rust has runtime: [0]. And so, usually, does C (in hosted implementations). [0] https://doc.rust-lang.org/reference/runtime.html
These are a couple of functions executables can call at run time, but they're more like an extra standard library. It's not a runtime in the same sense as a runtime in dynamic or GC languages that manages all objects and is able to know types of arbitrary objects and inspect/trace them. Rust has no run-time type information except limited downcasts via `dyn Any` or explicitly derived traits on per-type basis, and the…
Re: Native Reflection in Rust
#29Earlier 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…
To add to this, even the Foo-wrapper is gone, just the i32 remains. Rust values are amorphous data blobs at runtime.
Re: Native Reflection in Rust
#30* invokes local_type_id to get the memory address of your value’s static implementation of local_type_id
* maps that memory address to an offset in your application’s binary
* searches your application’s debug info for the entry describing the function at that offset
* parses that debugging information entry (DIE) to determine the type of local_type_id’s &self parameter.
This is a rather strange thing to bolt onto a language. I could see this as an external tool. The use case seems to be programs which used "async" so much they can't figure out the resulting state machine. External debug tools to view and examine the async state machine might be helpful.
My experience with Rust has been that debugging of safe code is just not a problem. Print statements and logging are enough.