如何在 Rust 中按值传递装箱特征对象?

2023-11-22

我正在编写一些代码,并且有一个方法的特征,该方法需要self按价值。我想调用这个方法Box'd 特征对象(消耗Box及其价值)。这可能吗?如果是这样,怎么办?

就代码而言,最小示例如下(不完整)代码:

trait Consumable {
    fn consume(self) -> u64;
}
fn consume_box(ptr: Box<dyn Consumable>) -> u64 {
    //what can I put here?
}

我的问题是如何填写函数consume_box具有指定的签名,以便返回的值是通过调用获得的任何值consume on the Box很值。

我最初写的是

ptr.consume()

作为函数的主体,尽管我意识到这不是一个正确的想法,因为它没有说明我想要的事实Box被消耗的不仅仅是它的内容,而是我唯一能想到的东西。这不会编译,给出错误:

无法移动 dyn Consumable 类型的值:无法静态确定 dyn Consumable 的大小

这对我来说有点令人惊讶,作为 Rust 的新手,我原以为也许self参数的传递方式类似于 C++ 中的右值引用(这确实是我想要的 - 在 C++ 中,我可能会通过带有签名的方法来实现它virtual std::uint64_t consume() &&,让一个std::unique_ptr通过虚拟析构函数清理移出的对象),但我猜 Rust 确实是按值传递,将参数移动到之前的位置 - 所以它拒绝代码是合理的。

问题是,我不知道如何获得我想要的行为,我可以在哪里消费Boxd 特质对象。我尝试使用默认实现向特征添加一个方法,认为这可能会在 vtable 中为我带来一些有用的东西:

trait Consumable {
    fn consume(self) -> u64;
    fn consume_box(me: Box<Self>) -> u64 {
        me.consume()
    }
}

但是,这会产生错误

特质Consumable不能被制成物体

当我提到Box<dyn Consumable>type - 这并不奇怪,因为编译器会计算出如何处理参数类型随参数类型变化的函数Self那就太神奇了。

是否可以实现该功能consume_box使用提供的签名 - 甚至在必要时修改特征?


如果它有用,更具体地说,这是某些数学表达式的某种表示的一部分 - 也许玩具模型将是大致如下所示的具体实现:

impl Consumable for u64 {
    fn consume(self) -> u64 {
        self
    }
}
struct Sum<A, B>(A, B);
impl<A: Consumable, B: Consumable> Consumable for Sum<A, B> {
    fn consume(self) -> u64 {
        self.0.consume() + self.1.consume()
    }
}
struct Product<A, B>(A, B);
impl<A: Consumable, B: Consumable> Consumable for Product<A, B> {
    fn consume(self) -> u64 {
        self.0.consume() * self.1.consume()
    }
}
fn parse(&str) -> Option<Box<dyn Consumable> > {
    //do fancy stuff
}

在大多数情况下,事物都是普通的旧数据(但由于泛型的原因,可能会出现任意大的数据块),但也希望它能够与传递更多不透明的句柄到此类事物兼容 - 因此希望能够与Box<dyn Consumable>。至少在语言层面上,这是我要做的事情的一个很好的模型 - 这些对象拥有的唯一资源是内存片段(与多线程无关,也没有自引用恶作剧) - 尽管这个模型没有捕捉到我所拥有的用例对于实现使用对象而不是仅仅读取它是有用的,它也没有适当地建模我想要一个可能段的“开放”类而不是有限集可能性(使得很难做类似的事情enum直接代表一棵树) - 因此为什么我询问按值传递而不是尝试重写它以按引用传递。


您可以从Box<dyn Trait>如果参数是self: Box<Self>:

trait Consumable {
    fn consume(self) -> u64;
    fn consume_box(self: Box<Self>) -> u64;
}

struct Foo;
impl Consumable for Foo {
    fn consume(self) -> u64 {
        42
    }
    fn consume_box(self: Box<Self>) -> u64 {
        self.consume()
    }
}

fn main() {
    let ptr: Box<dyn Consumable> = Box::new(Foo);
    println!("result is {}", ptr.consume_box());
}

然而,这确实有一个令人讨厌的样板,必须实现consume_box()对于每个实施;尝试定义默认实现会遇到“无法移动类型的值Self- 的大小Self不能静态确定” error.


一般来说,虽然这是不支持的。 Adyn Consumable代表一个unsized除了通过间接(通过引用或Box-类似结构)。它适用于上述情况,因为Box有点特别(是唯一可调度的您可以从中获取所有权的类型)和consume_box方法不放self作为动态特征对象放在堆栈上(仅在每个具体实现中)。

然而有RFC 1909:未调整大小的 RValue希望放宽其中一些限制。能够传递未指定大小的函数参数,例如self在这种情况下。该 RFC 的当前实现在每晚编译时接受您的初始代码unsized_fn_params:

#![feature(unsized_fn_params)]

trait Consumable {
    fn consume(self) -> u64;
}

struct Foo;
impl Consumable for Foo {
    fn consume(self) -> u64 {
        42
    } 
}

fn main () {
    let ptr: Box<dyn Consumable> = Box::new(Foo);
    println!("result is {}", ptr.consume());
}

请参阅操场.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 Rust 中按值传递装箱特征对象? 的相关文章

随机推荐

  • 如何为webpack设置多个环境变量?

    有谁知道如何向 webpack 提供多个环境变量 我试图运行以下脚本但没有成功 cross env NODE ENV production DTM ENV staging webpack config internals webpack p
  • 将数组从 jQuery 传递到 MVC.NET 控制器,在控制器上给出 null 结果,但 jQuery 函数上存在值

    我正在尝试将数组从 jQuery 函数传递到我的控制器 该数组包含内容和保存该内容的 div 的 id 当我检查通过 Firebug 中的 AJAX 发送的对象时 那里有正确的值 但在我的控制器上放置断点后 收到的值是一个空列表或数组或我尝
  • 使用gradle将jar上传到本地Maven仓库

    这个问题已经被问过好几次了 但不知何故我无法让它发挥作用 Gradle 是一个很棒的工具 但它的文档却一点也不出色 对于那些不每天使用它的人来说 没有任何例子几乎不可能理解它 我正在使用 Android Studio 我想将模块输出 jar
  • symfony2 注销

    我的问题是捕获用户注销 我所拥有的代码是 public function onAuthenticationFailure Request request AuthenticationException exception return ne
  • Angularjs:使用日期助手将时间戳格式化为 UTC 时间

    我正在寻找如何使用 AngularJS 日期助手格式化 unix 时间戳的想法 In 官方文档 只有字符串输入的解释 如果字符串输入中没有指定时区 则认为时间是本地时区 但是时间戳呢 有什么提示吗 我找到了一些带有自定义过滤器和 momen
  • 无法获取 Google Cloud Storage 存储桶的位置

    我尝试使用 Java 客户端将数据从 S3 传输到 GCS 但出现此错误 无法获取 Google Cloud Storage GCS 存储桶的位置 由于权限不足 请验证是否已授予必要的权限 我正在使用具有项目所有者角色的服务帐户 该帐户应授
  • onTouch MotionEvent getTouchMinor 和 getTouchMajor 的数字结果总是完全相同,为什么?

    查看文档 触摸主坐标和触摸副坐标是触摸事件的椭圆轴 一种是触摸区域的最长测量长度 另一种是触摸事件的最短测量长度 就像椭圆的测量一样 不过 我在几台 Android 平板电脑上测试了 getTouchMajor 和 getTouchMino
  • Magento 中多个模块覆盖同一个核心文件

    Magento 如何处理覆盖同一核心文件的多个模块 是否可以 它是如何完成的 您有 3 种解决冲突的选择 将一个冲突文件中的代码合并到另一个文件中 并关闭其中一个文件中的重写 config xml 关闭一个 config xml 中的重写
  • C++ 中的函数组合

    有许多令人印象深刻的 Boost 库 例如 Boost Lambda 或 Boost Phoenix 它们在使 C 成为真正的函数式语言方面大有帮助 但是是否有一种直接的方法可以从任意 2 个或更多任意函数或函子创建复合函数 如果我有 in
  • strstr() 在 gcc 和 VS 中的实现是否具有线性复杂度?

    我知道有快速的字符串搜索算法 例如博耶 摩尔 and 高德纳 莫里斯 普拉特 其复杂度为 O n m 而普通解决方案为 O n m 那么 最流行的工具链 gcc 和 Visual Studio 的 strstr 实现是使用这些快速 O n
  • css 过渡不透明度在元素显示:无然后更改为显示:块的情况下不起作用

    正如标题所说 我有这个代码 https jsfiddle net fwo9ym1o javascript var container document querySelector container container style disp
  • 如何在 subprocess.Popen() 中使用现有环境变量

    Scenario 在我的 python 脚本中 我需要将一个可执行文件作为子进程运行 其中包含可执行文件所期望的 x 个命令行参数 Example EG 1 myexec sh 参数1 参数2 EG 2 myexec sh param1 M
  • 了解 Matlab FFT 示例

    我是 matlab 和 FFT 新手 想了解Matlab FFT 示例 现在我有两个主要问题 1 为什么x轴 频率 以500结束 我怎么知道没有更多频率或者它们只是被忽略了 2 我如何知道频率在0到500之间 FFT 不应该告诉我频率的限制
  • 如何检测 `snprintf` 错误?

    int snprintf char restrict s size t n const char restrict format snprintf 很好地防止超出目的地s 然而 当目的地不足以获得完整结果时 如何检测该错误和其他错误 以下内
  • 为什么在 Windows 上创建新进程比在 Linux 上更昂贵?

    我听说在 Windows 机器上创建新进程比在 Linux 上更昂贵 这是真的 有人可以解释为什么它更昂贵的技术原因 并提供这些原因背后的设计决策的任何历史原因吗 mweerden NT 从第一天起就是为多用户设计的 所以这并不是一个真正的
  • 在C++中连接字符串流

    如何连接两个字符串流 include
  • 长按按钮

    我想重复一个动作Button长时间按下 例如 MP3 阅读器的前进按钮 WinForm 中是否存在现有的 c 事件 我可以处理MouseDown启动一个计时器的事件 该计时器将执行该操作并停止MouseUp事件 但我正在寻找一种更简单的方法
  • 无法连接到远程://localhost:9999。连接超时 Jboss 7.1.1 Final

    我正在端口 9999 将构建部署到本地和远程 Jboss AS 7 1 1 Final Maven 使用 jboss 插件 jboss as maven plugin 7 1 1 Final 来管理服务器的构建 我已确认服务器已启动并正在运
  • Python:QML 布局内的 matplotlib 绘图

    考虑以下 python3 PyQt 代码来显示带有工具栏的交互式 matplotlib 图形 import sys sip import numpy as np from PyQt5 import QtGui QtWidgets from
  • 如何在 Rust 中按值传递装箱特征对象?

    我正在编写一些代码 并且有一个方法的特征 该方法需要self按价值 我想调用这个方法Box d 特征对象 消耗Box及其价值 这可能吗 如果是这样 怎么办 就代码而言 最小示例如下 不完整 代码 trait Consumable fn co