Add Concrete examples for newtype and compose design

Add Concrete examples for newtype and compose design
pull/368/head
Owen Leung 1 year ago
parent 3a9e5de41c
commit d582e14a5f

@ -18,41 +18,30 @@ This creates a new type, rather than an alias to a type (`type` items).
## Example
```rust,ignore
// Some type, not necessarily in the same module or even crate.
struct Foo {
//..
}
impl Foo {
// These functions are not present on Bar.
//..
}
// The newtype.
pub struct Bar(Foo);
impl Bar {
// Constructor.
pub fn new(
//..
) -> Self {
use std::fmt::Display;
//..
// Create Newtype Password to override the Display trait for String
struct Password (String);
impl Display for Password {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "****************")
}
//..
}
fn main() {
let b = Bar::new(...);
// Foo and Bar are type incompatible, the following do not type check.
// let f: Foo = b;
// let b: Bar = Foo { ... };
let unsecured_password: String = "ThisIsMyPassword".to_string();
let secured_password: Password = Password(unsecured_password.clone());
println!("unsecond_password: {}", unsecured_password);
println!("secured_password: {}", secured_password);
}
```
```shell
unsecond_password: ThisIsMyPassword
secured_password: ****************
```
## Motivation
The primary motivation for newtypes is abstraction. It allows you to share

@ -20,51 +20,81 @@ Here is a contrived example of where the borrow checker foils us in our plan to
use a struct:
```rust
struct A {
f1: u32,
f2: u32,
f3: u32,
struct Database {
connection_string: String,
timeout: u32,
pool_size: u32,
}
fn foo(a: &mut A) -> &u32 { &a.f2 }
fn bar(a: &mut A) -> u32 { a.f1 + a.f3 }
fn print_database(database: &Database) {
println!("Connection string: {}", database.connection_string);
println!("Timeout: {}", database.timeout);
println!("Pool size: {}", database.pool_size);
}
fn main() {
let mut db = Database {
connection_string: "initial string".to_string(),
timeout: 30,
pool_size: 100,
};
fn baz(a: &mut A) {
// The later usage of x causes a to be borrowed for the rest of the function.
let x = foo(a);
// Borrow checker error:
// let y = bar(a); // ~ ERROR: cannot borrow `*a` as mutable more than once
// at a time
println!("{}", x);
let connection_string = &mut db.connection_string;
print_database(&db); // Immutable borrow of `db` happens here
*connection_string = "new string".to_string(); // Mutable borrow is used here
}
```
We can apply this design pattern and refactor `A` into two smaller structs, thus
solving the borrow checking issue:
We can apply this design pattern and refactor `Database` into three smaller
structs, thus solving the borrow checking issue:
```rust
// A is now composed of two structs - B and C.
struct A {
b: B,
c: C,
// Database is now composed of three structs - ConnectionString, Timeout and PoolSize.
// Let's decompose it into smaller structs
#[derive(Clone)]
struct ConnectionString {
value: String,
}
struct B {
f2: u32,
struct Timeout {
value: u32,
}
struct C {
f1: u32,
f3: u32,
struct PoolSize {
value: u32,
}
// These functions take a B or C, rather than A.
fn foo(b: &mut B) -> &u32 { &b.f2 }
fn bar(c: &mut C) -> u32 { c.f1 + c.f3 }
// We then compose these smaller structs back into `Database`
struct Database {
connection_string: ConnectionString,
timeout: Timeout,
pool_size: PoolSize,
}
// print_database can then take ConnectionString, Timeout and Poolsize struct instead
fn print_database(connection_str: ConnectionString,
timeout: Timeout,
pool_size: PoolSize) {
println!("Connection string: {}", connection_str.value);
println!("Timeout: {}", timeout.value);
println!("Pool size: {}", pool_size.value);
}
fn baz(a: &mut A) {
let x = foo(&mut a.b);
// Now it's OK!
let y = bar(&mut a.c);
println!("{}", x);
fn main() {
// Initialize the three structs
let connection_string = ConnectionString { value: "localhost".to_string() };
let timeout = Timeout { value: 30 };
let pool_size = PoolSize { value: 100 };
let mut db = Database {
connection_string,
timeout,
pool_size,
};
let connection_string = &mut db.connection_string;
print_database(connection_string.clone(), db.timeout, db.pool_size);
*connection_string = ConnectionString{ value:"new string".to_string() };
}
```

Loading…
Cancel
Save