Is it possible to get the name of a struct using one of its methods? [duplicate] - reflection

This question already has answers here:
How do I print in Rust the type of a variable?
(17 answers)
Closed 4 years ago.
For example:
struct ABC;
impl ABC {
fn some_method(&self) -> &str {
// return the name of its struct -> "ABC"
}
}
I'm writing Python extensions and I need a way to return the current struct's name for its repr method. In Python, I can get this using self.__class__.__name__. Is there anything similar in Rust?

It's possible with nightly and the core_intrinsics feature:
#![feature(core_intrinsics)]
use std::intrinsics::type_name;
struct ABC;
impl ABC {
fn some_method(&self) -> &'static str {
unsafe { type_name::<Self>() }
}
}
fn main() {
println!("{}", ABC.some_method()); // ABC
}

Related

How to create collections of a specific enum variant? [duplicate]

This question already has an answer here:
How to write a function that only accepts one enum variant as input?
(1 answer)
Closed 10 months ago.
I would like to create a vector of a specific variant of an enum in Rust. For example:
enum EnumExample {
VariantExample,
}
fn main() {
let singleEnum = EnumExample::VariantExample;
let vectorOfEnums = Vec::<EnumExample>::new();
let vectorOfEnumVariants = Vec::<EnumExample::VariantExample>::new();
}
But for vectorOfEnumVariants, I get the error error[E0573]: expected type, found variant 'EnumExample::VariantExample'. Is there a way to do this? Is there something else I should be doing instead?
I'm not aware of a way of constraining to an enum variant. But you can use inner types for your enums and create a Vec of those, then implement From so you can transform those into the enum variants whenever you need:
struct Variant;
enum EnumExample {
VariantExample(Variant),
}
impl From<Variant> for EnumExample {
fn from(v: Variant) -> Self {
Self::VariantExample(v)
}
}
fn main() {
let singleEnum = EnumExample::VariantExample;
let vectorOfEnums = Vec::<EnumExample>::new();
let vectorOfVariants = Vec::<Variant>::new();
let vectorOfEnumVariants: Vec<EnumExample> =
vectorOfVariants.into_iter().map(Into::into).collect();
}
Playground

Rust: Joining and iterating over futures' results

I have some code that iterates over objects and uses an async method on each of them sequentially before doing something with the results. I'd like to change it so that the async method calls are joined into a single future before being executed. The important bit below is in HolderStruct::add_squares. My current code looks like this:
use anyhow::Result;
struct AsyncMethodStruct {
value: u64
}
impl AsyncMethodStruct {
fn new(value: u64) -> Self {
AsyncMethodStruct {
value
}
}
async fn get_square(&self) -> Result<u64> {
Ok(self.value * self.value)
}
}
struct HolderStruct {
async_structs: Vec<AsyncMethodStruct>
}
impl HolderStruct {
fn new(async_structs: Vec<AsyncMethodStruct>) -> Self {
HolderStruct {
async_structs
}
}
async fn add_squares(&self) -> Result<u64> {
let mut squares = Vec::with_capacity(self.async_structs.len());
for async_struct in self.async_structs.iter() {
squares.push(async_struct.get_square().await?);
}
let mut sum = 0;
for square in squares.iter() {
sum += square;
}
return Ok(sum);
}
}
I'd like to change HolderStruct::add_squares to something like this:
use futures::future::join_all;
// [...]
impl HolderStruct {
async fn add_squares(&self) -> Result<u64> {
let mut square_futures = Vec::with_capacity(self.async_structs.len());
for async_struct in self.async_structs.iter() {
square_futures.push(async_struct.get_square());
}
let square_results = join_all(square_futures).await;
let mut sum = 0;
for square_result in square_results.iter() {
sum += square_result?;
}
return Ok(sum);
}
}
However, the compiler gives me this error using the above:
error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
--> src/main.rs:46:20
|
46 | sum += square_result?;
| ^^^^^^^^^^^^^^ the `?` operator cannot be applied to type `&std::result::Result<u64, anyhow::Error>`
|
= help: the trait `std::ops::Try` is not implemented for `&std::result::Result<u64, anyhow::Error>`
= note: required by `std::ops::Try::into_result`
How would I change the code to not have this error?
for square_result in square_results.iter()
Lose the iter() call here.
for square_result in square_results
You seem to be under impression that calling iter() is mandatory to iterate over a collection. Actually, anything that implements IntoIterator can be used in a for loop.
Calling iter() on a Vec<T> derefs to slice (&[T]) and yields an iterator over references to the vectors elements. The ? operator tries to take the value out of the Result, but that is only possible if you own the Result rather than just have a reference to it.
However, if you simply use a vector itself in a for statement, it will use the IntoIterator implementation for Vec<T> which will yield items of type T rather than &T.
square_results.into_iter() does the same thing, albeit more verbosely. It is mostly useful when using iterators in a functional style, a la vector.into_iter().map(|x| x + 1).collect().

Can I return a struct which uses PhantomData from a trait implementation to add a lifetime to a raw pointer without polluting the interface?

In this question someone commented that you could use PhantomData to add a lifetime bound to a raw pointer inside a struct. I thought I'd try doing this on an existing piece of code I've been working on.
Here's our (minimised) starting point. This compiles (playground):
extern crate libc;
use libc::{c_void, free, malloc};
trait Trace {}
struct MyTrace {
#[allow(dead_code)]
buf: *mut c_void,
}
impl MyTrace {
fn new() -> Self {
Self {
buf: unsafe { malloc(128) },
}
}
}
impl Trace for MyTrace {}
impl Drop for MyTrace {
fn drop(&mut self) {
unsafe { free(self.buf) };
}
}
trait Tracer {
fn start(&mut self);
fn stop(&mut self) -> Box<Trace>;
}
struct MyTracer {
trace: Option<MyTrace>,
}
impl MyTracer {
fn new() -> Self {
Self { trace: None }
}
}
impl Tracer for MyTracer {
fn start(&mut self) {
self.trace = Some(MyTrace::new());
// Pretend the buffer is mutated in C here...
}
fn stop(&mut self) -> Box<Trace> {
Box::new(self.trace.take().unwrap())
}
}
fn main() {
let mut tracer = MyTracer::new();
tracer.start();
let _trace = tracer.stop();
println!("Hello, world!");
}
I think that the problem with the above code is that I could in theory move the buf pointer out of a MyTrace and use if after the struct has died. In this case the underlying buffer will have been freed due to the Drop implementation.
By using a PhantomData we can ensure that only references to buf can be obtained, and that the lifetimes of those references are bound to the instances of MyTrace from whence they came.
We can proceed like this (playground):
extern crate libc;
use libc::{c_void, free, malloc};
use std::marker::PhantomData;
trait Trace {}
struct MyTrace<'b> {
#[allow(dead_code)]
buf: *mut c_void,
_phantom: PhantomData<&'b c_void>,
}
impl<'b> MyTrace<'b> {
fn new() -> Self {
Self {
buf: unsafe { malloc(128) },
_phantom: PhantomData,
}
}
}
impl<'b> Trace for MyTrace<'b> {}
impl<'b> Drop for MyTrace<'b> {
fn drop(&mut self) {
unsafe { free(self.buf) };
}
}
trait Tracer {
fn start(&mut self);
fn stop(&mut self) -> Box<Trace>;
}
struct MyTracer<'b> {
trace: Option<MyTrace<'b>>,
}
impl<'b> MyTracer<'b> {
fn new() -> Self {
Self { trace: None }
}
}
impl<'b> Tracer for MyTracer<'b> {
fn start(&mut self) {
self.trace = Some(MyTrace::new());
// Pretend the buffer is mutated in C here...
}
fn stop(&mut self) -> Box<Trace> {
Box::new(self.trace.take().unwrap())
}
}
fn main() {
let mut tracer = MyTracer::new();
tracer.start();
let _trace = tracer.stop();
println!("Hello, world!");
}
But this will give the error:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:53:36
|
53 | Box::new(self.trace.take().unwrap())
| ^^^^^^
|
note: first, the lifetime cannot outlive the lifetime 'b as defined on the impl at 46:1...
--> src/main.rs:46:1
|
46 | impl<'b> Tracer for MyTracer<'b> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...so that the types are compatible:
expected std::option::Option<MyTrace<'_>>
found std::option::Option<MyTrace<'b>>
= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the expression is assignable:
expected std::boxed::Box<Trace + 'static>
found std::boxed::Box<Trace>
I have three sub-questions:
Did I understand the motivation for PhantomData in this scenario correctly?
Where is 'static coming from in the error message?
Can this be made to work without changing the interface of stop? Specifically, without adding a lifetime to the return type?
I'm going to ignore your direct question because I believe you arrived at it after misunderstanding several initial steps.
I could in theory move the buf pointer out of a MyTrace and use if after the struct has died
Copy the pointer, not move it, but yes.
By using a PhantomData we can ensure that only references to buf can be obtained
This is not true. It is still equally easy to get a copy of the raw pointer and misuse it even when you add a PhantomData.
Did I understand the motivation for PhantomData in this scenario correctly?
No. PhantomData is used when you want to act like you have a value of some type without actually having it. Pretending to have a reference to something is only useful when there is something to have a reference to. There's no such value to reference in your example.
The Rust docs say something about raw pointers and PhantomData, but I perhaps got it wrong
That example actually shows my point well. The Slice type is intended to behave as if it has a reference to the Vec that it's borrowed from:
fn borrow_vec<'a, T>(vec: &'a Vec<T>) -> Slice<'a, T>
Since this Slice type doesn't actually have a reference, it needs a PhantomData to act like it has a reference. Note that the lifetime 'a isn't just made up out of whole cloth — it's related to an existing value (the Vec). It would cause memory unsafety for the Slice to exist after the Vec has moved, thus it makes sense to include a lifetime of the Vec.
why the commenter in the other question suggested I use PhantomData to improve the type safety of my raw pointer
You can use PhantomData to improve the safety of raw pointers that act as references, but yours doesn't have some existing Rust value to reference. You can also use it for correctness if your pointer owns some value behind the reference, which yours seemingly does. However, since it's a c_void, it's not really useful. You'd usually see it as PhantomData<MyOwnedType>.
Where is 'static coming from in the error message?
Why is adding a lifetime to a trait with the plus operator (Iterator<Item = &Foo> + 'a) needed?

How can I invoke an unknown Rust function with some arguments using reflection?

I'm having a lot of fun playing around with Rust having been a C# programmer for a long time but I have a question around reflection. Maybe I don't need reflection in this case but given that Rust is strongly typed I suspect I do (I would definitely need it in good ol' C#, bless its cotton socks).
I have this situation:
use std::collections::HashMap;
fn invoke_an_unknown_function(
hashmap: HashMap<String, String>,
// Something to denote a function I know nothing about goes here
) {
// For each key in the hash map, assign the value
// to the parameter argument whose name is the key
// and then invoke the function
}
How would I do that? I'm guessing I need to pass in some sort of MethodInfo as the second argument to the function and then poke around with that to get the arguments whose name is the key in the hash map and assign the values but I had a look around for the reflection API and found the following pre-Rust 1.0 documentation:
Module std::reflect
Module std::repr
[rust-dev] Reflection system
None of these give me enough to go on to get started. How would I implement the function I describe above?
Traits are the expected way to implement a fair amount of what reflection is (ab)used for elsewhere.
trait SomeInterface {
fn exposed1(&self, a: &str) -> bool;
fn exposed2(&self, b: i32) -> i32;
}
struct Implementation1 {
value: i32,
has_foo: bool,
}
impl SomeInterface for Implementation1 {
fn exposed1(&self, _a: &str) -> bool {
self.has_foo
}
fn exposed2(&self, b: i32) -> i32 {
self.value * b
}
}
fn test_interface(obj: &dyn SomeInterface) {
println!("{}", obj.exposed2(3));
}
fn main() {
let impl1 = Implementation1 {
value: 1,
has_foo: false,
};
test_interface(&impl1);
}

How do you share implementation details in a functional language like rust?

I sometimes find myself writing abstract classes with partial implementation in C#:
abstract public class Executor {
abstract protected bool Before();
abstract protected bool During();
abstract protected bool After();
protected bool Execute() {
var success = false;
if (Before()) {
if (During()) {
if (After()) {
success = true;
}
}
}
return success;
}
}
Notwithstanding the wisdom of such a control structure, how would I accomplish this (partial shared implementation) in a functional language like rust?
Using default methods on traits is one way (and will probably/hopefully be the idiomatic way in the future; until recently, the struct-with-closures method #Slartibartfast demonstrates was the only thing that actually worked):
#[allow(default_methods)];
trait Executable {
fn before(&self) -> bool;
fn during(&self) -> bool;
fn after(&self) -> bool;
fn execute(&self) -> bool {
self.before() && self.during() && self.after()
}
}
impl Executable for int {
fn before(&self) -> bool { *self < 10 }
fn during(&self) -> bool { *self < 5 }
fn after(&self) -> bool { *self < 0 }
// execute is automatically supplied, if it is not implemented here
}
Note that it is possible for an implementation of Executable to override execute at the moment (I've opened an issue about a #[no_override] attribute that would disable this).
Also, default methods are experimental and prone to crashing the compiler (yes, more so than the rest of Rust), but they are improving quickly.
I'm not within reach of a rust compiler, so forgive broken code.
On a functional side of things, you could make a struct that holds three functions and invoke them
struct Execution {
before: #fn() -> bool,
during: #fn() -> bool,
after: #fn() -> bool
}
fn execute (e: Execution) -> bool {
...
}
but once you have a function as a first class value, you could pass say, a list of boolean functions to check against instead of fixed three, or something else depending on what are you trying to achieve.
On a rust side of things, you can make it more "object oriented" by using traits
trait Executable {
fn execute(&self);
}
impl Execution {
fn execute(&self) {
...
}
}

Resources