-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a slide about struct visibility and encapsulation (#2522)
The current slide on visibility introduces the idea that modules are the encapsulation boundary, but it doesn't explicitly doesn't talk about struct fields/methods. I think it's worth talking explicitly about structs and encapsulation because most students are going to be coming from a background where types (instead of modules) are the encapsulation boundary. It's worth talking explicitly about how encapsulation works, even if we're kind of repeating information from the previous slide.
- Loading branch information
1 parent
2e8d5d3
commit f60513e
Showing
2 changed files
with
79 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
--- | ||
minutes: 5 | ||
--- | ||
|
||
# Visibility and Encapsulation | ||
|
||
Like with items in a module, struct fields are also private by default. Private | ||
fields are likewise visible within the rest of the module (including child | ||
modules). This allows us to encapsulate implementation details of struct, | ||
controlling what data and functionality is visible externally. | ||
|
||
```rust,editable | ||
use outer::Foo; | ||
mod outer { | ||
pub struct Foo { | ||
pub val: i32, | ||
is_big: bool, | ||
} | ||
impl Foo { | ||
pub fn new(val: i32) -> Self { | ||
Self { val, is_big: val > 100 } | ||
} | ||
} | ||
pub mod inner { | ||
use super::Foo; | ||
pub fn print_foo(foo: &Foo) { | ||
println!("Is {} big? {}", foo.val, foo.is_big); | ||
} | ||
} | ||
} | ||
fn main() { | ||
let foo = Foo::new(42); | ||
println!("foo.val = {}", foo.val); | ||
// let foo = Foo { val: 42, is_big: true }; | ||
outer::inner::print_foo(&foo); | ||
// println!("Is {} big? {}", foo.val, foo.is_big); | ||
} | ||
``` | ||
|
||
<details> | ||
|
||
- This slide demonstrates how privacy in structs is module-based. Students | ||
coming from object oriented languages may be used to types being the | ||
encapsulation boundary, so this demonstrates how Rust behaves differently | ||
while showing how we can still achieve encapsulation. | ||
|
||
- Note how the `is_big` field is fully controlled by `Foo`, allowing `Foo` to | ||
control how it's initialized and enforce any invariants it needs to (e.g. that | ||
`is_big` is only `true` if `val > 100`). | ||
|
||
- Point out how helper functions can be defined in the same module (including | ||
child modules) in order to get access to the type's private fields/methods. | ||
|
||
- The first commented out line demonstrates that you cannot initialize a struct | ||
with private fields. The second one demonstrates that you also can't directly | ||
access private fields. | ||
|
||
- Enums do not support privacy: Variants and data within those variants is | ||
always public. | ||
|
||
## More to Explore | ||
|
||
- If students want more information about privacy (or lack thereof) in enums, | ||
you can bring up `#[doc_hidden]` and `#[non_exhaustive]` and show how they're | ||
used to limit what can be done with an enum. | ||
|
||
- Module privacy still applies when there are `impl` blocks in other modules | ||
[(example in the playground)][1]. | ||
|
||
</details> | ||
|
||
[1]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=3e61f43c88de12bcdf69c1d6df9ab3da |