在 Rust 中使用结构体的生命周期的正确方法是什么?

2024-05-02

我想写这样的结构:

struct A {
    b: B,
    c: C,
}

struct B {
    c: &C,
}

struct C;

The B.c应该借自A.c.

A ->
  b: B ->
    c: &C -- borrow from --+
                           |
  c: C  <------------------+

这是我尝试过的:

struct C;

struct B<'b> {
    c: &'b C,
}

struct A<'a> {
    b: B<'a>,
    c: C,
}

impl<'a> A<'a> {
    fn new<'b>() -> A<'b> {
        let c = C;
        A {
            c: c,
            b: B { c: &c },
        }
    }
}

fn main() {}

但它失败了:

error[E0597]: `c` does not live long enough
  --> src/main.rs:17:24
   |
17 |             b: B { c: &c },
   |                        ^ borrowed value does not live long enough
18 |         }
19 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'b as defined on the method body at 13:5...
  --> src/main.rs:13:5
   |
13 |     fn new<'b>() -> A<'b> {
   |     ^^^^^^^^^^^^^^^^^^^^^

error[E0382]: use of moved value: `c`
  --> src/main.rs:17:24
   |
16 |             c: c,
   |                - value moved here
17 |             b: B { c: &c },
   |                        ^ value used here after move
   |
   = note: move occurs because `c` has type `C`, which does not implement the `Copy` trait

我已经阅读了有关所有权的 Rust 文档,但我仍然不知道如何修复它。


实际上,上面的代码失败的原因不止一个。让我们稍微分解一下,并探讨一些修复方法。

首先我们删除new并尝试构建一个实例A直接在main,这样你就会发现问题的第一部分与生命周期无关:

struct C;

struct B<'b> {
    c: &'b C,
}

struct A<'a> {
    b: B<'a>,
    c: C,
}

fn main() {
    // I copied your new directly here
    // and renamed c1 so we know what "c"
    // the errors refer to
    let c1 = C;

    let _ = A {
        c: c1,
        b: B { c: &c1 },
    };
}

这失败了:

error[E0382]: use of moved value: `c1`
  --> src/main.rs:20:20
   |
19 |         c: c1,
   |            -- value moved here
20 |         b: B { c: &c1 },
   |                    ^^ value used here after move
   |
   = note: move occurs because `c1` has type `C`, which does not implement the `Copy` trait

它说的是如果你分配c1 to c,您将其所有权移至c(即您无法再通过c1,仅通过c)。这意味着所有引用c1将不再有效。但你有一个&c1仍在范围内(在 B 中),因此编译器无法让您编译此代码。

当编译器指出该类型时,会在错误消息中提示可能的解决方案C是不可复制的。如果您可以复印一份C,那么您的代码将是有效的,因为分配c1 to c将创建该值的新副本,而不是移动原始副本的所有权。

我们可以做C可通过更改其定义来复制,如下所示:

#[derive(Copy, Clone)]
struct C;

现在上面的代码可以工作了。注意什么@matthieu-m 评论 https://stackoverflow.com/questions/27589054/what-is-the-correct-way-to-use-lifetimes-with-a-struct-in-rust#comment43603185_27589054仍然是这样:我们不能同时将值的引用和值本身存储在 B 中 https://stackoverflow.com/q/32300132/155423(我们在此处存储对值的引用和该值的副本)。但这不仅仅适用于结构,这也是所有权的运作方式。

现在,如果你不想(或不能)C可复制,您可以在两者中存储引用A and B反而。

struct C;

struct B<'b> {
    c: &'b C,
}

struct A<'a> {
    b: B<'a>,
    c: &'a C, // now this is a reference too
}

fn main() {
    let c1 = C;
    let _ = A {
        c: &c1,
        b: B { c: &c1 },
    };
}

那么一切都好吗?不是真的...我们仍然想移动创作A回到一个new方法。这就是我们会遇到一生麻烦的地方。让我们移动创作A回到一个方法:

impl<'a> A<'a> {
    fn new() -> A<'a> {
        let c1 = C;
        A {
            c: &c1,
            b: B { c: &c1 },
        }
    }
}

正如预期的那样,这是我们的一生错误:

error[E0597]: `c1` does not live long enough
  --> src/main.rs:17:17
   |
17 |             c: &c1,
   |                 ^^ borrowed value does not live long enough
...
20 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 13:1...
  --> src/main.rs:13:1
   |
13 | impl<'a> A<'a> {
   | ^^^^^^^^^^^^^^

error[E0597]: `c1` does not live long enough
  --> src/main.rs:18:24
   |
18 |             b: B { c: &c1 },
   |                        ^^ borrowed value does not live long enough
19 |         }
20 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 13:1...
  --> src/main.rs:13:1
   |
13 | impl<'a> A<'a> {
   | ^^^^^^^^^^^^^^

这是因为c1在结束时被摧毁new方法,因此我们无法返回对其的引用。

fn new() -> A<'a> {
    let c1 = C; // we create c1 here
    A {
        c: &c1,          // ...take a reference to it
        b: B { c: &c1 }, // ...and another
    }
} // and destroy c1 here (so we can't return A with a reference to c1)

一种可能的解决方案是创建C在外面new并将其作为参数传递:

struct C;

struct B<'b> {
    c: &'b C,
}

struct A<'a> {
    b: B<'a>,
    c: &'a C
}

fn main() {
    let c1 = C;
    let _ = A::new(&c1);
}

impl<'a> A<'a> {
    fn new(c: &'a C) -> A<'a> {
        A {c: c, b: B{c: c}}
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Rust 中使用结构体的生命周期的正确方法是什么? 的相关文章

随机推荐

  • 使用 UpdatePanel 的 ASP.NET AJAX

    从概念上讲 我对 AJAX 的理解是异步发送到服务器的请求 也称为并行 当我使用多个UpdatePanels在页面上并触发多个异步回发 例如通过使用按钮 我注意到第二个请求在第一个请求完成之前才开始 但是当我使用 JQuery ajax 并
  • 您可以bind()和connect() UDP连接的两端吗

    我正在编写一个点对点消息队列系统 它必须能够通过 UDP 运行 我可以任意选择一侧或另一侧作为 服务器 但这似乎不太正确 因为两端都从另一端发送和接收相同类型的数据 是否可以绑定 和连接 两端 以便它们只能彼此发送 接收 这似乎是一种非常对
  • WPF 的拖放列表框

    我正在寻找一个简单的ListBox具有内置的拖放功能 我认为 Silverlight 4 工具包有 The BoxList应该可以 通过拖放项目重新排序 从一个项目中拖动项目BoxList到另一个 显示拖动项目的预览 幽灵版本 显示放置位置
  • npx create-react-app myapp 命令抛出错误

    我想在 React 中创建一个应用程序 我已经安装了最新的 Node js 当我运行命令时出现错误 PS C Users Kumar Sanket Desktop React Redux gt npx create react app my
  • 对二进制数的字符串表示进行按位运算 python 2.7

    我想对二进制数的两个字符串表示执行按位或 但我不知道如何将字符串转换为原始二进制 a 010110 b 100000 a b 应该产生 110110 然后我想计算 on 位的数量 这应该返回 4 您可以使用内置的将字符串转换为二进制int
  • ggplot2:从纵横比中排除图例

    I use ggplot2 and knitr发布带有右侧图例的散点图 图例包含在纵横比中 因此破坏了绘图的 方形 如图所示默认主题 https github com hadley ggplot2 wiki themes 当图例文本变得比
  • Flutter中向TabView添加选项卡标签

    我正在尝试扩展作为答案提出的 TabViewhere https stackoverflow com questions 50036546 how to create a dynamic tabbarview render a new ta
  • 当属性值在 HTML5 中可以保持不带引号时

    HTML5 中什么时候属性值可以保持不带引号 HTML4 01 是一个 SGML 应用程序 因此 在 HTML4 中 如果值中使用的唯一字符是当前声明为名称字符的字符 字母数字字符 句号 则可以省略引号 好吧 来自 W3C 工作草案 201
  • Angular2 找不到命名空间“google”

    我正在与angular2 google maps以及最新版本的 Angular2 我正在尝试将一些本地地图组件功能转换为自己文件中的服务maps service ts 例如 地图组件 ts getGeoLocation lat number
  • 让 Selenium 与 Bootstrap 模式淡入淡出配合的建议?

    我正在努力以 BDD 的方式生活 我正在使用 Cucumber 带有 Selenium 并且碰巧在我的应用程序中使用 Twitter Bootstrap 模式 在运行 Cucumber 测试时 我得到了 Selenium WebDriver
  • 错误 - 仅返回类型不同的函数不能重载。由 小码哥发布于

    我正在尝试创建一个图书馆管理系统 我收到一些我不明白的错误 我在 Mac 操作系统中使用 Eclipse 我的主要代码是 include
  • MongoDB 聚合查询与 MySQL SELECT field1 FROM 表

    我对 MongoDB 完全陌生 想要比较 NoSQL 数据模型相对于关系数据库对应部分的查询性能 我将其写入 MongoDB shell Make 10 businesses Each business has 10 locations E
  • 未找到“google\appengine\CreateUploadURLRequest”类

    我正在使用谷歌云CORE PHP使用简单的 HTML 表单上传文件但我被困在CloudStorageTools班级 它会抛出连续的跟随错误 致命错误 类 找不到 google appengine api cloud storage Clou
  • Mac OS 上的 pybluez 安装错误

    我尝试安装pybluez使用以下命令 pip install pybluez sudo easy install pybluez 但对于这两个命令我最终都会出错 环境 Mac OSX 10 9 1 Python 2 7 点日志 cc fno
  • 如何指定登录表单链接的返回 URL?

    看起来很简单 但事实并非如此 主要是因为视图不可能知道通过模型和控制器到达那里的方式 无论如何 这是一个需要解决的问题 我有一个登录链接 它将用户带到一个表单以输入用户名和密码 当用户单击 提交 时 我想重定向到他正在查看的页面 最简单的方
  • 如何实现 IFilter 来索引重量级格式?

    我需要为 Microsoft Search Server 2008 开发一个 IFilter 它执行长时间的计算来提取文本 从一个文件中提取文本可能需要 5 秒到 12 小时 我如何设计这样的 IFilter 以便守护进程不会在超时时重置它
  • 如何在Android模拟器中找到数据库文件的路径?

    我正在 Android 模拟器中执行一些 sqlite 查询 我想知道数据库文件存储在哪个路径 请给我一些如何找到它的想法 如果你能给我一些代码片段 乌尔 s 库马兰 数据库通常存储在 data data your applications
  • Karma 与 Webpack 和 Typescript 不执行任何测试

    我试图弄清楚如何将 Karma 测试运行器与 Webpack 和 Typescript 源文件一起使用 以此源文件作为唯一的测试文件为例 测试规格 var message string yay alert message describe
  • android中textview截断一个字母

    这是我的 Android 设备的屏幕截图 文字是 asd 然而 d 被稍微切断了 这是相关视图
  • 在 Rust 中使用结构体的生命周期的正确方法是什么?

    我想写这样的结构 struct A b B c C struct B c C struct C The B c应该借自A c A gt b B gt c C borrow from c C lt 这是我尝试过的 struct C struc