错误"关闭的真正含义可能比当前功能更长?"?

时间:2018-03-08 02:21:10

标签: rust closures lifetime

我写了以下内容来测试闭包:

fn main() {
    let captured_val = "a captured value".to_string();
    let closure_parameter = "a parameter value".to_string();

    let mut ct = ClosureTest {
        cls: |closure_parameter| &captured_val,
    };
    println!("{}", (ct.cls)(&closure_parameter));
}

struct ClosureTest<T>
where
    T: FnMut(&str) -> &str,
{
    cls: T,
}

我收到了以下编译错误:

error[E0373]: closure may outlive the current function, but it borrows `captured_val`, which is owned by the current function
 --> src/main.rs:6:14
  |
6 |         cls: |closure_parameter| &captured_val,
  |              ^^^^^^^^^^^^^^^^^^^  ------------ `captured_val` is borrowed here
  |              |
  |              may outlive borrowed value `captured_val`
help: to force the closure to take ownership of `captured_val` (and any other referenced variables), use the `move` keyword
  |
6 |         cls: move |closure_parameter| &captured_val,
  |              ^^^^^^^^^^^^^^^^^^^^^^^^

我在struct中添加了一个生命周期参数,它编译并运行良好:

struct ClosureTest<'a, T>
where
    T: FnMut(&str) -> &'a str,
{
    cls: T,
}

两个变量(captured_valclosure_parameter)都在同一范围内,但似乎编译器没有看到它们具有相同的生命周期而没有添加生命周期参数'aClosureTest<T>。这是因为Rust的封闭期终生推断?

我不理解我收到的错误消息中的第一行:

error[E0373]: closure may outlive the current function, but it borrows `captured_val`, which is owned by the current function

封闭怎么能比现在的功能更长久?闭包是在当前函数中定义的,所以我认为闭包将在当前函数的末尾消失。

错误消息的真正含义是什么?为什么通过像我一样添加生命周期参数来解决?

1 个答案:

答案 0 :(得分:1)

简短版本:添加显式生命周期注释会使{strong>保证约为ClosureTest,如果没有它,则不一定暗示。

长版本

假设你这样做了:

fn main() {
    let closure_parameter = "a parameter value".to_string();

    let returned_ct = do_something(v);

    println!("{}", (returned_ct.cls)(&closure_parameter));
}

fn do_something() -> ClosureTest {
    let captured_val = "a captured value".to_string();

    let mut ct = ClosureTest {
        cls: |param| &captured_val,
    };
    ct // !!!
}

struct ClosureTest<T>
where
    T: FnMut(&str) -> &str,
{
    cls: T,
}

在标有!!!的行上,请注意ct 已移动作为返回值。现在的对象是“生活”。在main中,但它包含对do_something结束时删除的内容的引用。 ct.cls正在将引用返回给captured_val,如果将ct移出该功能,该引用将不再存在。

通过修改ClosureTest以包含生命周期,您将说明以下内容:

  • ClosureTest对象obj有一些生命周期'a
  • str返回的obj.cls引用的有效期为'a objobj.cls
  • 因此,ClosureTest返回的任何引用都与返回它的obj具有相同的范围。 ClosureType<'a, T> where T: FnMut(&str) -> &'a str将与其闭包引用的对象或更早同时删除。换句话说,任何T只能在对象ct的返回引用存在时生存,并且在删除该对象时必须删除。

借用检查程序不会因原始值而烦恼,因为captured_valuecaptured_val 的生命周期不同,因为他们可能有不同的生命时间 - 因此&#39;可能&#39;在&#39;中可能比借来的价值captured_val更长久。当您在示例中添加lifetime参数时,借用检查器现在可以确认它只应在ctmain一样长的时间内进行编译,它会在String orgWhere = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?"; ArrayList<ContentProviderOperation> opsUpdate = new ArrayList<>(); opsUpdate.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) .withSelection(orgWhere, new String[]{String.valueOf(contactID), ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE}) .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phoneTextInputLayout.getEditText().getText().toString()) .build()); opsUpdate.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) .withSelection(orgWhere, new String[]{String.valueOf(contactID), ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE}) .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, emailTextInputLayout.getEditText().getText().toString()) .build()); opsUpdate.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) .withSelection(orgWhere, new String[]{String.valueOf(contactID), ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE}) .withValue(ContactsContract.CommonDataKinds.Website.URL, websiteTextInputLayout.getEditText().getText().toString()) .build()); opsUpdate.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) .withSelection(orgWhere, new String[]{String.valueOf(contactID), ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE}) .withValue(ContactsContract.CommonDataKinds.Note.NOTE, notesTextInputLayout.getEditText().getText().toString()) .build()); opsUpdate.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) .withSelection(orgWhere, new String[]{String.valueOf(contactID), ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE}) .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, nameTextInputLayout.getEditText().getText().toString()) .build()); if (professionTextInputLayout2.getEditText().getText().toString().isEmpty()){ opsUpdate.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) .withSelection(orgWhere, new String[]{String.valueOf(contactID), ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE}) .withValue(ContactsContract.CommonDataKinds.Organization.DATA4, professionTextInputLayout.getEditText().getText().toString()) .build()); } else { opsUpdate.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) .withSelection(orgWhere, new String[]{String.valueOf(contactID), ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE}) .withValue(ContactsContract.CommonDataKinds.Organization.DATA4, professionTextInputLayout.getEditText().getText().toString() + "/" + professionTextInputLayout2.getEditText().getText().toString()) .build()); } opsUpdate.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) .withSelection(orgWhere, new String[]{String.valueOf(contactID), ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE}) .withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, locationTextInputLayout.getEditText().getText().toString()) .build()); try { ContentProviderResult[] res = this.getContentResolver().applyBatch( ContactsContract.AUTHORITY, opsUpdate); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (OperationApplicationException e) { // TODO Auto-generated catch block e.printStackTrace(); } 结束时删除它们(它们都在{ {1}})。