# Rust++: Object-Oriented Programming for Rust!!
[<img alt="crates.io" src="https://img.shields.io/crates/v/plusplus.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/plusplus)
[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-plusplus-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/plusplus)
This crate provides a `class!` macro that allows you to write Rust in an object-oriented style.
As an example:
```rust
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.