Перемещение

В Ü есть возможность в некоторых случаях заменить затратные операции копирования операциями перемещения. Перемещение - это смена местоположения объекта в адресном пространстве без смены содержимого объекта.

Перемещение происходит в тех случаях, когда перемещаемый объект существует здесь и сейчас в единственном экземпляре. Объект, существующий здесь и сейчас, создаётся через конструирование объекта или как результат вызова функции. При перемещении происходит опциональный вызов деструктора для назначения (если там уже есть объект) и простом побайтовом копировании содержимого перемещаемого объекта в место назначения.

Перемещение может произойти в следующих случаях:

  • Передаче аргумента-значения в функцию
  • Присваивании
  • Инициализации
  • Возврате значения
struct S{ i32 x = 0; }

fn GetS() : S
{
    // Создаётся временный объект типа "S", после чего он перемещается в возвращаемое значение.
    return S();
}

fn TakeS( S s ){}

fn Foo()
{
    // Создаётся временный объект типа "S", после чего он перемещается в переменную "s0" при её инициализации.
    auto s0= S();
    // Временное значение, полученное в результате вызова функции, перемещается при инициализации в переменную "s1".
    var S s1(GetS());
    var S mut s2;
    // Создаётся временный объект типа "S", после чего он перемещается в переменную "s2". Перед перемещением вызывается деструктор для "s2".
    s2= S();
    // Создаётся временный объект типа "S", после чего он перемещается в аргумент функции.
    TakeS( S() );
}

Оператор «move»

Перемещение происходит только в случаях, когда объект существует здесь и сейчас. Если же объект является стековой переменной или её частью, аргументом функции и т. д. Он просто так перемещаться не будет. Но существует способ заставить его переместиться - оператор move. Оператор move принимает в качестве аргумента имя локальной переменной или аргумента функции и возвращает значение, существующее здесь и сейчас. Перемещённая переменная при этом считается уничтоженной и перестаёт быть доступной. Деструктор для перемещённой переменной вызываться не будет.

struct S{ i32 x = 0; }

fn TakeS( S s ){}

fn Foo( S mut s_arg )
{
    var S mut s0, mut s1;
    // Произойдёт копирование локальной переменной "s0"
    TakeS( s0 );
    // Произойдёт перемещение локальной переменной "s1".
    TakeS( move(s1) );
    // Произойдёт перемещение аргумента функции.
    TakeS( move(s_arg) );
    // Деструктор вызовется только для переменной "s0", но не для переменных "s1" и "s_arg".
}

У оператора перемещения есть ряд ограничений. Перемещать можно только изменяемые локальные переменные. Нельзя переместить ссылки, члены композитных локальных переменных, неизменяемые локальные переменные. Нельзя перемещать внутри цикла внешнюю по отношению к циклу переменную. Нельзя условно перемещать переменную - перемещение должно происходить во всех ветвях одного if-else.

Оператор «take»

Данный оператор отчасти похож на оператор move, но имеет ряд различий. Он принимает любое выражение, не только имя переменной. Результат этого выражения он перемещает, конструируя в месте перемещения значение по умолчанию. Если тип перемещаемого значения не конструируем по умолчанию, будет порождена ошибка.

struct S
{
    i32 x;
    fn constructor() ( x= 0 ) {}
    fn constructor( i32 in_x ) ( x= in_x ) {}
}
fn Foo()
{
    var [ S, 3 ] mut arr[ (55), (77), (99) ];
    var S s= take(arr[1]); // Значение в "arr[1]" будет перемещено в новую переменную "s". Для "arr[1]" будет вызван конструктор по умолчанию.
}