Inspection

Objective: Extend class by a method that will print names and types of the instance variables.

Python

Based on type hints:

class Inspectable:
    def show_vars(self):
        for field_name, field_type in self.__annotations__.items():
            print(f"{field_name!r} : {field_type.__name__}")

class Foo(Inspectable):
    bar: int
    baz: str

foo = Foo()
foo.show_vars()

Based the values of variables:

class Inspectable:
    def show_vars(self):
        for field_name in dir(self):
            if not (field_name.startswith('__') and field_name.endswith('__')):
                field_value = getattr(self, field_name)
                if not callable(field_value):
                    print(f"{field_name!r} : {field_value.__class__.__name__}")

class Foo(Inspectable):
    def __init__(self):
        self.bar = 123
        self.baz = "qwx"

foo = Foo()
foo.show_vars()

Note that there are more corner cases to be considered in Python. For instance how to differentiate instance variables and methods. Whether dunder variables should be considered or not, etc.

Rust

// inspired by: https://stackoverflow.com/a/56389650

macro_rules! generate_struct {
    ($name:ident {$($field_name:ident : $field_type:ty),+}) => {
        struct $name { $($field_name: $field_type),+ }
        impl $name {
            fn show_vars(&self) {
            $(
            let field_name = stringify!($field_name);
            let field_type = stringify!($field_type);
               println!("{:?} : {:?}", field_name, field_type);
            )*
            }
        }
    };
}

generate_struct! { Foo { bar: i32, baz: String } }

fn main() {
    let foo = Foo{bar: 123, baz: "abc".to_string()};
    foo.show_vars();
}

Crystal

class Object
  def vars
    {{ @type.instance_vars.map {|v| v.name.stringify + " : " + v.type.stringify} }}
  end
  
  def show_vars
    self.vars.each {|v| puts v}
  end
end

class Foo
  def initialize(@bar : Int32, @baz : String)
  end
end

foo = Foo.new 123, "abc"
foo.show_vars()