plusplus-macros 0.1.1

Procedural macro implementation for plusplus crate
Documentation

Rust++: Object-Oriented Programming for Rust!!

This crate provides a class! macro that allows you to write Rust in an object-oriented style. As an example:

use plusplus::{class, InConstruction, DowncastTo};

// define a class
class!{
    pub class ObjectZero {
        string: String;

        // a class constructor. Classes returned from Rust++ class constructors must be
        // parameterized with the `<InConstruction>` type parameter.
        pub fn new() -> ObjectZero<InConstruction> {
            // classes are initialized in the constructor with the `init_class!` syntax.
            //
            // this takes all the class's fields as an argument, plus a `superclass` field for
            // initializing the superclass (when present)
            init_class! {
                string: "hello".into(),
                another_field: 26,
            }
        }

        // fields can have normal Rust visibility modifiers and be defined
        // anywhere in the class
        pub(crate) another_field: i32;

        pub fn set_string(&mut self, str: String) {
            self.string = str;
        }
    }
}

// object inheritance works across module and crate boundaries
mod object_one {
    use plusplus::{class, InConstruction};
    class!{
        // ObjectOne is a subclass of ObjectZero. it inherits all of ObjectZero's methods and
        // fields, and can extend ObjectZero's methods with new behavior
        pub class ObjectOne: super::ObjectZero {
            string_one: String;

            pub fn new() -> ObjectOne<InConstruction> {
                init_class! {
                    // initialize the superclass
                    superclass: super::ObjectZero::new(),

                    string_one: String::new(),
                }
            }

            pub fn print_example(&self) {
                println!("hi from ObjectOne");
            }

            // you override a superclass's methods by declaring an Override block with the name
            // of the class you're overriding
            override super::ObjectZero {
                pub fn set_string(&mut self, str: String) {
                    // you prefix method invocations with the `super_` prefix to call the
                    // parent's implementation of the method. this allows you to extend methods
                    // with new behaviors!
                    self.super_set_string(str.clone());
                    self.string_one = str;
                }
            }
        }
    }

    // if you want to define methods that can't be overridden, just put them in a normal
    // impl block
    impl ObjectOne {
        pub fn string_one(&self) -> &str {
            &self.string_one
        }
    }
}
use object_one::ObjectOne;

class!{
    pub class ObjectTwo: ObjectOne {
        string_two: String;
        pub fn new(two: impl Into<String>) -> ObjectTwo<InConstruction> {
            init_class! {
                superclass: object_one::ObjectOne::new(),
                string_two: two.into(),
            }
        }

        // in order to override a class's method, you must create an override block
        // for the class that declared that method
        override ObjectOne {
            pub fn print_example(&self) {
                self.super_print_example();
                println!("hello from ObjectTwo");
            }
        }

        // you can override methods from any parent class in the class hierarchy
        override ObjectZero {
            pub fn set_string(&mut self, str: String) {
                self.super_set_string(str.clone());
                self.string_two = str;
            }
        }
    }
}

fn main() {
    // initialize an object. the `finish` method wraps the class in a `ClassBox`
    // and allows it to behave polymorphically.
    let object_two = ObjectTwo::new("hello!").finish();

    // prints:
    // ```
    // hi from ObjectOne
    // hello from ObjectTwo
    // ```
    object_two.print_example();
    println!();

    let object_as_one = object_two.upcast(); // is ClassBox<ObjectOne>

    // calling a class method from anywhere in the class hierarchy will always result in the
    // deepest implementation of that method getting executed. so, this also prints:
    // ```
    // hi from ObjectOne
    // hello from ObjectTwo
    // ```
    object_as_one.print_example();
    println!();

    // you can call a method with the `my_` prefix in order to bypass the method overload and
    // call that class's own implementation of the method. so this prints:
    // ```
    // hi from ObjectOne
    // ```
    object_as_one.my_print_example();
    println!();

    let mut object_as_base = object_as_one.upcast(); // is ClassBox<ObjectZero>

    // this call results in all three `set_string` methods getting invoked
    let new_str = "sets all strings!!";
    object_as_base.set_string(new_str.to_string());

    // you can't directly access a subclass's fields from a superclass. so, the following line
    // would not compile:
    // println!("{}" object_as_base.string_two);

    let object_as_two = object_as_base
        .downcast_to::<ObjectOne>().unwrap()
        .downcast_to::<ObjectTwo>().unwrap();

    // but, you can access a superclass's fields, within the limits set by visibilty rules
    assert_eq!(object_as_two.string, new_str);
    assert_eq!(object_as_two.string_one(), new_str);
    assert_eq!(object_as_two.string_two, new_str);
}

More details, and how this works, can be found in the documentation.