Caution

You’re reading a draft of the Ferrocene Language Specification. Some parts of this document might be missing, incomplete or incorrect. Our aim is to have the specification ready by the end of 2022.

15. Ownership and Destruction

15.1. Ownership

Legality Rules

15.1:1 Ownership is a property of values that is central to the resource management model of Rust.

15.1:2 An owner is a value holder that holds a value.

15.1:3 A value shall have only one owner.

15.2. Initialization

Legality Rules

15.2:1 A value holder is either a constant, a static, or a variable.

15.2:2 Initialization is the act of supplying an initial value to a value holder.

15.2:3 When a value holder holds a value, the value holder is considered to be initialized.

15.2:4 When a value holder lacks a value or its value has been transferred by move, the value holder is considered to be uninitialized.

15.2:5 A value holder shall be initialized before it is accessed.

Runtime Semantics

15.2:6 All memory starts as uninitialized.

Examples

15.2:7 Variable a is initialized.

let a: i32 = 42;

15.2:8 Variable b starts off as uninitialized, but is later initialized by virtue of the assignment statement.

let b: i32;
b = 42;

15.2:9 Variable c starts off initialized, but is later uninitialized by virtue of a transfer by move.

use core::sync::atomic::AtomicI32;

let c: AtomicI32 = AtomicI32::new(42);
let d: AtomicI32 = c;

15.3. References

Legality Rules

15.3:1 A reference is a value of a reference type. A reference can be obtained explicitly by using a borrow expression or implicitly in certain scenarios.

15.3:2 A referent is the value pointed-to by a reference.

15.3:3 A reference shall point to an initialized referent.

15.3:4 The lifetime of a referent shall be at least as long as the lifetime of its reference.

15.3:5 A reference is active from the point of obtaining its referent upto the last use of the reference, prior to another assignment to the reference or the end of the scope of the reference.

15.3:6 A referent shall not be passed by move while a reference to it is active.

15.3:7 A referent shall not be modified while a reference to it is active.

15.3:8 An immutable reference is a value of a shared reference type, and prevents the mutation of its referent.

15.3:9 A mutable reference is a value of a mutable reference type, and allows the mutation of its referent.

15.3:10 The referent of an immutable reference shall be mutated only when the type of the `:t:`referent is subject to interior mutability.

15.3:11 While a mutable reference is active, no other reference shall refer to a value that overlaps with the referent of the mutable reference.

Examples

let immutable_reference: &i32 = &42;
let mutable_reference: &mut i32 = &mut 42;

15.4. Borrowing

Legality Rules

15.4:1 Borrowing is the process of temporarily associating a reference with a value without transferring ownership permanently.

15.4:2 A borrow is a reference produced by borrowing.

15.4:3 An implicit borrow is a borrow that is not present syntactically in program text. An implicit borrow occurs in the following contexts:

15.4:11 An implicit borrow may be an immutable borrow or a mutable borrow if required.

15.4:12 An immutable borrow is an immutable reference produced by borrowing.

15.4:13 A mutable borrow is a mutable reference produced by borrowing.

15.4:14 Borrowing a field of a union type borrows all remaining fields using the same lifetime.

15.4:15 Immutably borrowing a value proceeds as follows:

  1. 15.4:16 ??? (this should describe the order of borrowing and when the borrow is returned)

  2. 15.4:17 An immutable borrow of type &'a T is created, where lifetime 'a is replaced by a lifetime inference variable, and T is replaced by the borrowed type.

  3. 15.4:18 Lifetime inference is performed.

  4. 15.4:19 The immutable borrow is checked against other borrows and by move passing within the enclosing item.

15.4:20 Uniquely immutably borrowing a value proceeds as follows:

  1. 15.4:21 ???

15.4:22 Mutably borrowing a value proceeds as follows:

  1. 15.4:23 A mutable borrow of type &'a mut T is created, where lifetime 'a is replaced by a lifetime inference variable, and T is replaced by the borrowed type.

  2. 15.4:24 Lifetime inference is performed.

  3. 15.4:25 The mutable borrow is checked against other borrows and by move passing within the enclosing item.

Examples

let mutable_borrow = &mut 42;
let immutable_borrow = &42;

15.4:26 Variable x is captured using a unique immutable borrow.

let x = &mut 42;
let unique_immutable_borrow = || *x = 1;

15.5. Passing Conventions

Legality Rules

15.5:1 A passing convention is a mechanism by which a value is passed to and from a function.

15.5:2 A value is subject to a passing convention when the value is

15.5:9 A by copy type is a type that implements the core::marker::Copy trait.

15.5:10 A value of a by copy type is passed by copy. Passing by copy does not change the owner of the value.

15.5:11 A by move type is a type that does not implement the core::marker::Copy trait.

15.5:12 A value of a by move type is passed by move. Passing by move changes the owner of the value.

15.5:13 Passing by value is either passing by copy or passing by move.

15.5:14 A value of a reference type is passed by reference. Passing by reference temporarily changes the owner of the value.

15.5:15 Passing by immutable reference is passing by reference where the value is immutable.

15.5:16 Passing by unique immutable reference is passing by immutable reference where it is asserted that the reference to the value is the only live reference.

15.5:17 Passing by mutable reference is passing by reference where the value is mutable.

Dynamic Semantics

15.5:18 Passing a value by copy from a source owner to a target owner proceeds as follows:

  1. 15.5:19 The core::marker::Copy::clone(&value) function of the source owner is invoked.

  2. 15.5:20 The result of core::marker::Copy::clone is assigned to the target owner.

15.5:21 Passing a value by move from a source owner to a target owner proceeds as follows:

  1. 15.5:22 The value is unassigned from the source owner.

  2. 15.5:23 The value is assigned to the target owner.

15.5:24 Passing a value by reference from a source owner to a target owner proceeds as follows:

  1. 15.5:25 The value is unassigned from the source owner.

  2. 15.5:26 The value is assigned to the target owner.

  3. 15.5:27 Once the context of the target owner completes, then

    1. 15.5:28 The value is unassigned from the target owner.

    2. 15.5:29 The value is assigned back to the source owner.

Examples

15.5:30 Type i32 is a by copy type. By the end of the second let statement, x is the owner of the original 42 and y is the owner of a cloned 42.

let x: i32 = 42;
let y: i32 = x;

15.5:31 Type core::sync::atomic::AtomicI32 is a by move type. By the end of the second let statement, x is uninitialized and y is the sole owner of the atomic 42.

use core::sync::atomic::AtomicI32;

let x: AtomicI32 = AtomicI32::new(42);
let y: AtomicI32 = x;

15.5:32 Type &i32 is a by reference type. By the end of the second statement, x is the owner of the original 42.

fn add_one(value: &i32) -> i32 {
    *value + 1
}

let x: i32 = 42;
let y: i32 = add_one(&x);

15.6. Destruction

Legality Rules

15.6:1 Destruction is the process of recovering resources associated with a value as it goes out of scope.

15.7. Destructors

Legality Rules

15.7:1 A drop type is a type that implements the core::ops::Drop trait or contains a field that has a drop type.

15.7:2 A destructor is a function that is invoked immediately before the destruction of a value of a drop type.

15.7:3 Dropping a value is the act of invoking the destructor of the related type. Such an object is said to be dropped.

15.7:4 An uninitialized value holder is not dropped.

Dynamic Semantics

15.7:5 Dropping an initialized value holder proceeds as follows:

  1. 15.7:6 If the drop type implements the core::ops::Drop trait, then core::ops::Drop::drop of the drop type is invoked.

  2. 15.7:7 If the drop type is an array type, then its elements are dropped from the first element to the last element.

  3. 15.7:8 Otherwise, if the drop type is a closure type, then all capture targets whose capture mode is by move mode are dropped in unspecified order.

  4. 15.7:9 Otherwise, if the drop type is an enum type, then the fields of the active enum variant are dropped in declaration order.

  5. 15.7:10 Otherwise, if the drop type is a slice type, then its elements are dropped from the first element to the last element.

  6. 15.7:11 Otherwise, if the drop type is a struct type, then its fields are dropped in declaration order.

  7. 15.7:12 Otherwise, if the drop type is a trait object type, then the destructor of the underlying type is invoked.

  8. 15.7:13 Otherwise, if the drop type is a tuple type, then its fields are dropped in declaration order.

  9. 15.7:14 Otherwise, dropping has no effect.

Examples

struct PrintOnDrop(&'static str);

impl core::ops::Drop for PrintOnDrop {
    fn drop(&mut self) {
        println!("{}", self.0);
    }
}

15.7:15 When object array is dropped, its destructor drops the first element, then the second element.

let array = [PrintOnDrop("first element to be dropped"),
             PrintOnDrop("second element to be dropped")];

15.7:16 Object uninitialized is not dropped.

let uninitialized: PrintOnDrop;

15.8. Drop Scopes

Legality Rules

15.8:1 A drop scope is a region of program text that governs the dropping of objects. When control flow leaves a drop scope, all objects associated with that drop scope are dropped based on a drop order.

15.8:2 A drop construct is a construct that employs a drop scope. The following constructs are drop constructs:

15.8:7 Drop scopes are nested within one another as follows:

15.8:17 A binding declared in a let statement is associated with the drop scope of the block expression that contains the let statement.

15.8:18 A binding declared in a match expression is associated with the drop scope of the match arm of the match expression.

15.8:19 A binding declared in an if let expression is associated with the drop scope of the block expression of the if let expression.

15.8:20 A binding declared in a while let loop expression is associated with the drop scope of the block expression of the while let loop expression.

15.8:21 A binding declared in a for loop expression is associated with the drop scope of the block expression of the for loop expression.

15.8:22 A value or binding of a function parameter is associated with the drop scope of the function of the function parameter.

15.8:23 A temporary that is not subject to constant promotion is associated with the innermost drop scope that contains the expression which produced the temporary, taking into account drop scope extension. The possible drop scopes are as follows:

15.8.1. Drop Scope Extension

Legality Rules

15.8.1:1 Drop scope extension is the process of extending a drop scope associated with a temporary to prevent the premature dropping of the temporary.

15.8.1:2 An extending pattern is either

15.8.1:5 If the pattern-without-alternation of a let statement is an extending pattern, then the drop scope of the expression of the let statement is extended to the drop scope of the block expression that contains the let statement.

15.8.1:6 An extending expression is either

15.8.1:10 The drop scope of the operand of a borrow expression that is an extending expression is extended to the drop scope of the block expression that contains the let statement.

15.8.1:11 The drop scope of the operand of a borrow expression, a dereference expression, or a field access expression that has an extended drop scope is extended to the drop scope of the expression.

15.8.1:12 The drop scope of the indexed operand of an index expression that has an extended drop scope is extended to the drop scope of the expression.

Examples

15.8.1:13 See Paragraph 15.6.1. for the declaration of PrintOnDrop.

15.8.1:14 The drop scope of the temporary created for expression AtomicI32::new(42) is extended to the drop scope of the block expression.

use core::sync::atomic::AtomicI32;

{
    let ref mut a = AtomicI32::new(42);
    println!("{}", a);
}

15.9. Drop Order

Legality Rules

15.9:1 Drop order is the order by which objects are dropped when a drop scope is left.

15.9:2 When a drop scope is left, all objects associated with that drop scope are dropped as follows:

  • 15.9:3 Bindingss are dropped in reverse declaration order.

  • 15.9:4 Temporaries <temporary> are dropped in reverse creation order.

15.9:5 When a drop scope of a function is left, then each function parameter is dropped from right to left as follows:

15.9:8 When multiple drop scopes are left at once, the objects are dropped from the innermost drop scope to the outermost drop scope.

Examples

15.9:9 See Paragraph 15.6.1. for the declaration of PrintOnDrop.

15.9:10 The drop order of the following variables is b, c, a. Dropping proceeds as follows:

  1. 15.9:11 The scope of the block expression is left first because it is an inner scope.

  2. 15.9:12 b is dropped.

  3. 15.9:13 The outer scope is left.

  4. 15.9:14 c is dropped because dropping occurs in reverse declarative order.

  5. 15.9:15 a is dropped.

let a = PrintOnDrop("3");
{
    let b = PrintOnDrop("1");
}
let c = PrintOnDrop("2");