-
Notifications
You must be signed in to change notification settings - Fork 19
Description
Chapter
FFI
Guideline Title
Document lifetime, ownership and reentrancy of external functions and their arguments
Category
Required
Status
Draft
Release Begin
1.0.0
Release End
latest
FLS Paragraph ID
fls_yztwtek0y34v
Decidability
Undecidable
Scope
Module
Tags
reduce-human-error, undefined-behavior
Amplification
No response
Exception(s)
In case the function does not expose any requirements on lifetime, ownership and concurrency, the function shall be marked as safe
. In this case, no documentation on these aspects needs to be provided.
Rationale
Since the ownership and lifetime information of function arguments cannot be conveyed safely over the C FFI boundary of Rust, such information needs to be well documented on the Rust side since such functions need to be marked unsafe
and the caller has to ensure the validity of these properties. In addition, the function's reentrancy and concurrency requirements need to be known to the caller. This includes interference with other functions and shall also elaborate on dependencies of these concurrency requirements to the data the function receives and/or returns.
Non-Compliant Example - Prose
In the example, there are multiple things that are unclear to the user of the function:
- What are the lifetime constraints of
cell
? Will it be consumed by the function or would one be able to add the data to another cell as well? - How long will the pointer be valid which is returned by the function?
- Is it ok to call this function from multiple threads? If yes, will it be ok to do so even when using a pointer to the same spreadsheet?
Non-Compliant Example - Code
unsafe extern "C" {
fn set_cell_value(spreadsheet: *mut Spreadsheet, cell: *const CellData) -> *mut SpreadsheetCell);
}
Compliant Example - Prose
In the good example there are statements on lifetime, ownership and behavior under concurrency. This allows for the design of a safe Rust-like wrapper that encodes the required constraints to achieve a safe interface that does not expose any UB because the underlying adapter respects all prose-described constraints.
Compliant Example - Code
unsafe extern "C" {
/// Updates or adds a cell to the spreadsheet.
///
/// # Concurrency
/// It is ok to call the method in parallel, provided that each call refers to a distinct spreadsheet. Calls from different threads with the same spreadsheet need to be synchronized.
///
/// # Parameters
/// `spreadsheet` points to a valid instance of `Spreadsheet`
/// ' cell` points to an instance of `CellData` that has previously been obtained by a call to `create_cell_data`. The referred object must not be used anymore after the call.
///
/// The function returns a pointer to the updated cell. The pointer will be valid as long as `spreadsheet` is valid or until a call to any function that modifies the object referred to by `spreadsheet`.
fn set_cell_value(spreadsheet: *mut Spreadsheet, cell: *const CellData) -> *mut SpreadsheetCell);
}