Skip to content

Commit 5d0a89e

Browse files
Fix Box<T> large-data example and clarify ownership transfer
1 parent 9c4d00a commit 5d0a89e

1 file changed

Lines changed: 87 additions & 37 deletions

File tree

courses/rust_essentials/140_smart_pointers/02_box_t.rst

Lines changed: 87 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ What Is "Box<T>"
88

99
- Allocates data on the heap (via :rust:`Box::new`)
1010

11-
- Stores a fixed-size pointer on the stack
11+
- Stores a fixed-size pointer on the stack
1212

13-
- Retains single ownership of heap data
13+
- Retains single ownership of heap data
14+
15+
- Deallocates memory automatically when object goes out of scope
1416

15-
- Deallocates memory automatically when object goes out of scope
16-
1717
- Defined in **prelude**
1818

1919
.. code:: rust
@@ -23,27 +23,27 @@ What Is "Box<T>"
2323
2424
// Implicit dereference
2525
println!("Box value is {}", my_box);
26-
26+
2727
.. code:: output
2828
2929
Box value is 5
30-
30+
3131
------------------------------------
3232
Using "Box<T>" for Recursive Types
3333
------------------------------------
3434

3535
- Types must have a known size at compile time
3636

3737
- Recursive types don't have a known size
38-
38+
3939
.. code:: rust
4040
4141
// FAILS: How big is an infinite doll?
4242
enum Doll {
4343
Inside(Doll),
4444
Empty,
4545
}
46-
46+
4747
.. code:: error
4848
:font-size: small
4949
@@ -67,42 +67,92 @@ Using "Box<T>" for Recursive Types
6767
Handling Large Data
6868
---------------------
6969

70-
- :rust:`Box<T>` allows transferring ownership of data
71-
72-
- Rather than copying data passed in parameters for function calls
73-
74-
- Useful for large data
75-
76-
.. code:: rust
77-
78-
struct BigData {
79-
samples: [u64; 1000000],
80-
metadata: String,
70+
- :rust:`Box::new(large_value)` can still require a large stack temporary
71+
72+
- Do not rely on compiler optimizations to avoid stack overflow
73+
74+
- Initialize the elements on the heap with :rust:`vec!`
75+
76+
- Convert the vector into an owned slice with :rust:`into_boxed_slice()`
77+
78+
.. code:: rust
79+
80+
fn create_data() -> Box<[u64]> {
81+
vec![0_u64; 1_000_000].into_boxed_slice()
82+
}
83+
84+
- Returning the box transfers ownership; the elements stay in place
85+
86+
.. note::
87+
88+
This avoids a large stack temporary, but heap allocation can still fail
89+
90+
----------------------------------
91+
Choosing a Heap-Allocated Buffer
92+
----------------------------------
93+
94+
- :rust:`Vec<u64>` already owns its heap buffer and is cheap to move
95+
96+
- Keep the vector if the buffer needs to grow or shrink
97+
98+
- :rust:`Box<[u64]>` owns a heap-allocated slice with a fixed length
99+
100+
- The length is stored at runtime, unlike :rust:`Box<[u64; 1_000_000]>`
101+
102+
- A fixed length does not make the elements immutable
103+
104+
- Moving either container does not relocate its heap-allocated elements
105+
106+
- Moving a large inline value may still relocate its bytes
107+
108+
.. note::
109+
110+
:rust:`into_boxed_slice()` may reallocate to discard excess capacity, this conversion is different from moving an existing box
111+
112+
------------------------------------
113+
Borrowing or Transferring Ownership
114+
------------------------------------
115+
116+
- Borrow with :rust:`&[u64]` when temporary access is enough
117+
118+
- The caller retains ownership; the buffer is not copied
119+
120+
- Move :rust:`Box<[u64]>` when another value must own the buffer
121+
122+
- Ownership is transferred without copying the buffer
123+
124+
.. code:: rust
125+
126+
fn inspect_data(samples: &[u64]) {
127+
println!("{} samples", samples.len());
81128
}
82-
let huge_chunk = Box::new(BigData {
83-
samples: [0; 1000000],
84-
metadata: String::from("Satellite Telemetry - Region A"),
85-
});
86-
// Only reference is moved to the function...
87-
// ...not the whole array
88-
process_data(huge_chunk);
89-
129+
struct DataProcessor {
130+
samples: Box<[u64]>,
131+
}
132+
133+
let samples = create_data();
134+
inspect_data(&samples); // Borrow; 'samples' is still usable
135+
136+
let processor = DataProcessor { samples }; // Move the box
137+
// 'samples' is no longer usable; 'processor' owns the buffer
138+
println!("{} samples", processor.samples.len());
139+
140+
.. note::
141+
142+
Dropping :rust:`processor` drops its boxed slice and frees the buffer, :rust:`&samples` coerces to :rust:`&[u64]`
143+
90144
---------------------
91145
Resource Management
92146
---------------------
93147

94-
- :rust:`Box<T>` implements :rust:`Drop` to ensure memory safety
148+
- :rust:`Box<T>` implements :rust:`Drop` to ensure memory safety
95149

96150
- Invokes :rust:`Drop` method automatically at end of scope
97-
151+
98152
- No need for manual intervention
99-
153+
100154
- Prevents memory leaks by ensuring deallocation
101-
102-
- Transferring ownership is an *O(1)* operation
103-
104-
- Regardless of what it points to
105-
106-
107155

108-
156+
- Transferring ownership is an *O(1)* operation
157+
158+
- Regardless of what it points to

0 commit comments

Comments
 (0)