We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Macros, FFI and Type-Driven Design step 26 of 28
Nullable pointer optimization, callbacks and qsort
Complete a size report and two sorting functions that call the C library's
qsort. Keep the supplied foreign declaration and sorting wrappers, and
fix their Rust callbacks.
Required results
sort_by_abs_desc(v: Vec<i32>) -> Vec<i32>: sort by descending absolute value, breaking equal-magnitude ties by ascending signed value.[3, -5, 5, -3, 0]becomes[-5, 5, -3, 3, 0].sort_pairs(v: Vec<(i32, i32)>) -> Vec<(i32, i32)>: sort by the second field ascending, then the first field ascending.[(3, 1), (1, 2), (2, 1)]becomes[(2, 1), (3, 1), (1, 2)].niche_report() -> Vec<(String, usize)>: return the ten named size measurements in the order shown below, usingsize_of.
Preserve duplicates. Empty and single-element inputs are unchanged.
Sorting must handle the full i32 range, including i32::MIN.
Measure Option representation
A niche is a representation that cannot be a valid value of a type.
Rust guarantees that certain Option types use such a representation for
None without increasing the size. For example, a function pointer cannot
be null, and NonZeroU32 cannot be zero.
| Report key | Type | Expected bytes on the exercise's 64-bit target |
|---|---|---|
extern_fn |
extern "C" fn() |
8 |
option_extern_fn |
Option<extern "C" fn()> |
8 |
ref_u8 |
&u8 |
8 |
option_ref_u8 |
Option<&u8> |
8 |
box_u8 |
Box<u8> |
8 |
option_box_u8 |
Option<Box<u8>> |
8 |
nonzero_u32 |
NonZeroU32 |
4 |
option_nonzero_u32 |
Option<NonZeroU32> |
4 |
const_ptr_u8 |
*const u8 |
8 |
option_const_ptr_u8 |
Option<*const u8> |
16 |
These are five type/Option pairs. The first four have the documented
same-size guarantee. A raw pointer can already be null, so
Some(null) and None must remain distinct. Its Option does not have
the same guarantee; the 16-byte measurement is target-specific.
A nullable C callback can be represented by Option<extern "C" fn(...)>,
with the correct argument and return types. Use an unsafe function-pointer
type if calling it requires the caller to uphold unsafe preconditions.
Our qsort callback is required, so the declaration uses a bare
unsafe extern "C" fn and always passes a valid function.
Equal size alone does not prove ABI compatibility. A general
Option<*const T> is not a C nullable-pointer representation, and not
every type with a niche is automatically suitable for FFI. Check the
documented representation and the ownership and validity rules of the
specific boundary.
The qsort contract
The wrapper passes a live vector buffer, an element count, an element size
in bytes, and a comparator. The buffer must remain valid and available
for qsort to rearrange throughout the call. Here i32 and Pair are
plain Copy values, and the callback only reads them.
The comparator receives pointers to two elements. Return a negative value
when the first should come before the second, zero when they compare equal,
and a positive value when it should come after. qsort does not require
exactly -1 and 1, but casting Rust's Ordering to c_int provides
those values conveniently.
Comparisons must be consistent across the sort. Equal comparison results for distinct elements are allowed: sorting only by magnitude is a valid ordering, but it does not satisfy this exercise's signed tie-break. qsort is not stable, so it does not promise the original relative order of elements the comparator considers equal. An inconsistent comparator violates the sorting contract and must not be used.
Compare without overflow
Do not implement the ordering by subtracting values. Subtraction can overflow, and wrapping can produce the wrong comparison sign.
Use unsigned_abs() for magnitudes. The magnitude of i32::MIN is
2,147,483,648, which fits in u32 but not in i32. Compare magnitudes
in reverse order for descending output, then compare the original signed
values in ascending order.
For pairs, compare (first.b, first.a) with
(second.b, second.a). Tuple comparison is lexicographic: it considers
the second component only when the first components are equal.
The callbacks must not unwind across this extern "C" boundary.
An escaping Rust panic aborts the process. The supplied pointer reads
rely on qsort providing valid, aligned pointers to the declared element
type; the rest of each comparator can use non-panicking comparisons.
Callback ABI and pair layout
A bare function pointer has no captured environment. An ordinary
non-capturing closure can coerce to a Rust fn pointer, but that does
not give it the required C ABI. Use the supplied
unsafe extern "C" fn callbacks. APIs that support stateful callbacks
usually provide a separate context pointer; standard qsort does not.
The opaque-pointer lesson
covers passing context explicitly.
The wrapper converts tuples into a #[repr(C)] Pair buffer and passes
size_of::<Pair>(). Keep that representation and size in agreement.
However, qsort itself treats elements as opaque bytes: it does not read
the Pair fields. A repr(C) layout would be essential if C code also
interpreted those fields. Sorting opaque Rust tuples through matching
Rust callbacks is not inherently forbidden merely because tuples lack
a C field-layout guarantee.
Keep the len() > 1 guards. They avoid an unnecessary foreign call and
keep empty-vector pointer considerations outside this wrapper.
The starter's magnitude ordering is ascending and lacks the requested tie-break; its pair ordering starts with the wrong field. Fix these comparisons and replace the zero placeholders in the report with actual measurements. Passing outputs is evidence for these cases, not a general proof of FFI safety.
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.