Skip to main content

readme_example/
readme_example.rs

1use plusplus::{class, InConstruction, DowncastTo};
2
3// define a class
4class!{
5    // this line is necessary to make the macro compile properly in the `examples` directory but
6    // not necessary in your code.
7    crate as plusplus;
8
9    pub class ObjectZero {
10        string: String;
11
12        // a class constructor. Classes returned from Rust++ class constructors must be
13        // parameterized with the `<InConstruction>` type parameter.
14        pub fn new() -> ObjectZero<InConstruction> {
15            // classes are initialized in the constructor with the `init_class!` syntax.
16            //
17            // this takes all the class's fields as an argument, plus a `superclass` field for
18            // initializing the superclass (when present)
19            init_class! {
20                string: "hello".into(),
21                another_field: 26,
22            }
23        }
24
25        // fields can have normal Rust visibility modifiers and be defined
26        // anywhere in the class
27        pub(crate) another_field: i32;
28
29        pub fn set_string(&mut self, str: String) {
30            self.string = str;
31        }
32    }
33}
34
35// object inheritance works across module and crate boundaries
36mod object_one {
37    use plusplus::{class, InConstruction};
38    class!{
39        crate as plusplus;
40        // ObjectOne is a subclass of ObjectZero. it inherits all of ObjectZero's methods and
41        // fields, and can extend ObjectZero's methods with new behavior
42        pub class ObjectOne: super::ObjectZero {
43            string_one: String;
44
45            pub fn new() -> ObjectOne<InConstruction> {
46                init_class! {
47                    // initialize the superclass
48                    superclass: super::ObjectZero::new(),
49
50                    string_one: String::new(),
51                }
52            }
53
54            pub fn print_example(&self) {
55                println!("hi from ObjectOne");
56            }
57
58            // you override a superclass's methods by declaring an Override block with the name
59            // of the class you're overriding
60            override super::ObjectZero {
61                pub fn set_string(&mut self, str: String) {
62                    // you prefix method invocations with the `super_` prefix to call the
63                    // parent's implementation of the method. this allows you to extend methods
64                    // with new behaviors!
65                    self.super_set_string(str.clone());
66                    self.string_one = str;
67                }
68            }
69        }
70    }
71
72    // if you want to define methods that can't be overridden, just put them in a normal
73    // impl block
74    impl ObjectOne {
75        pub fn string_one(&self) -> &str {
76            &self.string_one
77        }
78    }
79}
80use object_one::ObjectOne;
81
82class!{
83    crate as plusplus;
84    pub class ObjectTwo: ObjectOne {
85        string_two: String;
86        pub fn new(two: impl Into<String>) -> ObjectTwo<InConstruction> {
87            init_class! {
88                superclass: object_one::ObjectOne::new(),
89                string_two: two.into(),
90            }
91        }
92
93        // in order to override a class's method, you must create an override block
94        // for the class that declared that method
95        override ObjectOne {
96            pub fn print_example(&self) {
97                self.super_print_example();
98                println!("hello from ObjectTwo");
99            }
100        }
101
102        // you can override methods from any parent class in the class hierarchy
103        override ObjectZero {
104            pub fn set_string(&mut self, str: String) {
105                self.super_set_string(str.clone());
106                self.string_two = str;
107            }
108        }
109    }
110}
111
112fn main() {
113    // initialize an object. the `finish` method wraps the class in a `ClassBox`
114    // and allows it to behave polymorphically.
115    let object_two = ObjectTwo::new("hello!").finish();
116
117    // prints:
118    // ```
119    // hi from ObjectOne
120    // hello from ObjectTwo
121    // ```
122    object_two.print_example();
123    println!();
124
125    let object_as_one = object_two.upcast(); // is ClassBox<ObjectOne>
126
127    // calling a class method from anywhere in the class hierarchy will always result in the
128    // deepest implementation of that method getting executed. so, this also prints:
129    // ```
130    // hi from ObjectOne
131    // hello from ObjectTwo
132    // ```
133    object_as_one.print_example();
134    println!();
135
136    // you can call a method with the `my_` prefix in order to bypass the method overload and
137    // call that class's own implementation of the method. so this prints:
138    // ```
139    // hi from ObjectOne
140    // ```
141    object_as_one.my_print_example();
142    println!();
143
144    let mut object_as_base = object_as_one.upcast(); // is ClassBox<ObjectZero>
145
146    // this call results in all three `set_string` methods getting invoked
147    let new_str = "sets all strings!!";
148    object_as_base.set_string(new_str.to_string());
149
150    // you can't directly access a subclass's fields from a superclass. so, the following line
151    // would not compile:
152    // println!("{}" object_as_base.string_two);
153
154    let object_as_two = object_as_base
155        .downcast_to::<ObjectOne>().unwrap()
156        .downcast_to::<ObjectTwo>().unwrap();
157
158    // but, you can access a superclass's fields, within the limits set by visibilty rules
159    assert_eq!(object_as_two.string, new_str);
160    assert_eq!(object_as_two.string_one(), new_str);
161    assert_eq!(object_as_two.string_two, new_str);
162}