We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Unsafe and Soundness step 4 of 24
Safety comments as a discipline: // SAFETY: and # Safety
Copy a byte range into a new vector, documenting why each unsafe operation meets its requirements.
pub unsafe fn copy_run(src: &[u8], from: usize, len: usize) -> Vec<u8>
Copy len bytes starting at from, preserving their order. For example,
src = [1, 2, 3, 4, 5], from = 1 and len = 3 produces [2, 3, 4].
The caller must guarantee that from + len does not overflow and is at most
src.len(). The harness supplies valid ranges, including empty ranges at
the end and an empty source. Invalid ranges are outside this unsafe API's
contract; the function does not need to return an error for them.
Use Vec::with_capacity, ptr::copy_nonoverlapping and Vec::set_len.
For this exercise, do not use indexing, extend_from_slice or to_vec to
perform the copy. Keep the supplied lint settings enabled. Output tests
cannot enforce every implementation choice or establish soundness.
Document the caller's responsibilities
A public unsafe function needs a # Safety section explaining what its
caller must establish. For copy_run, state the range and addition-overflow
requirements. This is a caller obligation, not a claim that the function
checks the arguments itself.
Unsafe traits also need documented requirements for implementations and use. The relevant contract depends on the API; do not copy a generic safety paragraph without checking what it promises.
Explain why each operation is valid here
Put a // SAFETY: comment immediately before each unsafe block. Explain
how this code meets that operation's requirements, using the caller's
contract and facts established by the implementation.
For example, the final length update needs both initialized bytes and sufficient capacity:
// SAFETY: the copy initialized the first `len` bytes, and
// with_capacity(len) reserved space for at least that many bytes.
A comment such as // SAFETY: this is fine gives a reviewer nothing specific
to verify. A detailed comment is useful evidence of the intended argument,
but the comment itself does not prove the code correct. It can be mistaken
or become outdated when code changes.
If a requirement cannot be justified, investigate before proceeding. The justification may come from a check in this function, a caller guarantee or a type invariant. A check in another function is not automatically wrong; what matters is whether its guarantee still applies at this operation.
Understand the lint feedback
The starter enables several Clippy lints, and the grader treats warnings as errors:
| Lint | What to address |
|---|---|
missing_safety_doc |
Document the public unsafe function's caller obligations. |
undocumented_unsafe_blocks |
Add a safety justification for each unsafe block. |
multiple_unsafe_ops_per_block |
Separate unsafe operations into individual blocks for this exercise. |
unnecessary_safety_comment |
Avoid using a safety marker for an operation that needs no unsafe justification. |
unnecessary_safety_doc |
Do not attach an unsafe caller contract to a safe function. |
missing_safety_doc is enabled by default; the other listed lints are
explicitly enabled in the template. Lints check code structure and comment
presence, not the truth of the safety argument. Exact diagnostics and comment
recognition can vary with the Clippy version. Keep comments adjacent to the
operations they explain rather than relying on formatting edge cases.
A nested call can contain more than one unsafe operation. For example,
from_raw_parts_mut(ptr.add(mid), n) calls both the unsafe pointer-offset
method and the unsafe slice constructor. Separate them when this lint is
enabled, and justify the requirements of each. Splitting a block does not
by itself make either operation sound.
src.as_ptr() and out.as_mut_ptr() are safe calls and do not need safety
markers. However, raw-pointer operations are not all safe until dereference:
add already has requirements when the new pointer is computed.
Justify the three steps
Compute the source pointer. The caller's range promise gives
from <= src.len(). For a nonzero byte offset, the original pointer and the
traversed range must belong to the same allocation, and the offset must fit
in isize. A valid byte slice provides the required size bound. The result
may point just past the range's end when no bytes will be read. An offset of
zero also handles the empty slice without requiring an allocation behind it.
Copy into spare capacity. For a nonempty copy, the source range is
readable and initialized because it lies inside src. The new vector has
writable capacity for at least len bytes and its allocation is separate
from the source, so the ranges do not overlap. Both pointers have the
alignment required for u8. The count is in elements; it equals a byte
count here because the element type is u8.
For a zero-length copy, neither vector needs allocated storage. The pointers still satisfy the copy operation's alignment requirements, and both copied ranges are empty. Do not describe this case as requiring two allocations.
Set the initialized length. Call set_len(len) only after the copy.
The requested length must be within capacity, and the newly included
elements must already be initialized. Reserving capacity alone does not
initialize them.
Do not reverse the copy and length update, even if tests appear to pass.
That violates set_len's contract. For element types with destructors,
incorrect initialization tracking can also make cleanup access invalid
elements. This exercise copies u8; generalizing a bitwise copy to arbitrary
owning types requires a separate ownership argument.
For more context, see validity and safety invariants and leak amplification. Passing compilation, tests and Clippy supports the review; it does not replace checking the unsafe contracts.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.