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}