2015-05-29 4 views
6

Я полагаю, что этот вопрос касается жизни в целом, но у меня возникают трудности с закрытием, потому что вы не можете записать их тип.Как объявить замыкание, которое живет дольше, чем его охватывающий блок

Этот пример немного надуманный - я только начинаю изучать Ржавчину, и это то, что меня повесили.

Эта программа не скомпилируется:

fn main() { 
    let mut list: Vec<&Fn() -> i32> = Vec::new(); 

    { 
     list.push(&|| 1); 
    } 
} 

Потому что:

src/main.rs:5:25: 5:24 error: borrowed value does not live long enough 
src/main.rs:5   list.push(&|| 1); 
           ^~~~ 
src/main.rs:2:50: 7:2 note: reference must be valid for the block suffix following statement 0 at 2:49... 
src/main.rs:2  let mut list: Vec<&Fn() -> i32> = Vec::new(); 
src/main.rs:3 
src/main.rs:4  { 
src/main.rs:5   list.push(&move || 1); 
src/main.rs:6  } 
src/main.rs:7 } 
src/main.rs:5:9: 5:26 note: ...but borrowed value is only valid for the statement at 5:8 
src/main.rs:5   list.push(&|| 1); 
         ^~~~~~~~~~~~~~~~~ 
src/main.rs:5:9: 5:26 help: consider using a `let` binding to increase its lifetime 
src/main.rs:5   list.push(&|| 1); 
         ^~~~~~~~~~~~~~~~~ 

То, что я понял из этой ошибки в том, что срок службы закупоривающего ограничивается заявление внутри блока, но должен жить для всего тела main.

Я знаю (или, я думаю), что передача закрытия в push в качестве ссылочного означает, что push заимствует только закрытие, и это право собственности будет возвращено блоку. Этот код будет работать, если я могу просто дать закрытие push (т. Е. Если push взял на себя ответственность за закрытие), но поскольку закрытие не имеет размера, я должен передать его как ссылку.

Это правильно? Как я могу заставить этот код работать?

ответ

8

Есть две вещи, которые просят о:

  1. задающие TypeName за то, что не имеет specifyable TypeName
  2. позволяя укупорочное жить дольше, чем блок, где она определена.

Первая проблема исправлена ​​НЕ указывая имя типа и позволяя делать вывод о ржавчине.

let mut list: Vec<_> = Vec::new(); 

Вторая проблема решена, не пытаясь сделать укупорочное жить дольше, но, сделав его «по значению», так что вы можете переместить его. Это гарантирует, что ваше закрытие ничего не ссылается, но владеет всеми зафиксированными значениями.

for i in 0..10 { 
    list.push(move || i); 
} 

Теперь это дает нам новую проблему. Если мы добавим другое закрытие в Vec, типы не будут совпадать. Поэтому для достижения этого нам нужно закрыть окно.

fn main() { 
    let mut list: Vec<Box<Fn() -> i32>> = Vec::new(); 

    for i in 0..10 { 
     list.push(Box::new(move|| i)); 
    } 

    { 
     list.push(Box::new(move|| 42)); 
    } 
} 
6

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

Если вы хотите заем на то, чтобы длиться в течение любого заданного периода, вы должны заимствовать из памяти, которая будет длиться по крайней мере так долго. В этом случае, поскольку вы хотите сохранить заем в Vec, это означает, что независимо от того, какое хранилище вы занимаете, необходимо пережить Vec, а также. Таким образом:

fn main() { 
    let closure; 
    let mut list: Vec<&Fn() -> i32> = Vec::new(); 

    { 
     closure = || 1; 
     list.push(&closure); 
    } 
} 

Обратите внимание, что closure определяется до тогоlist есть. В Rust значения теряются в обратном лексическом порядке в конце их области действия, поэтому любая переменная, определенная послеlist, обязательно будет отброшена до нее, что приведет к list, содержащему недопустимые указатели.

Если вы хотите нажать несколько затворов, вам понадобится отдельная переменная для каждого из них.

Чтобы предотвратить возможную «мою фактическую проблему не так просто», добавление (: P): f вам нужно вернуть list или каким-то образом сохранить его за пределами одного вызова функции, обратите внимание, что не способ продлить заем. В этом случае вам необходимо изменить list на вектор , в штучной упаковке (, т. Е.Vec<Box<Fn() -> i32>>).

Смежные вопросы