Rust 中的表达式模板实现类似于 boost::yap

2024-05-23

我正在尝试自学 Rust,作为一个具有挑战性的学习项目,我想复制 C++ 表达式模板库的设计模式提升::雅普 https://www.boost.org/doc/libs/1_74_0/doc/html/yap.html。我不需要一个完整的实现,我只是想要一个小型演示器来了解 Rust 的泛型是否足够强大以实现它并在此过程中学习一些东西。

我提出了一个想法,但目前陷入困境。我的问题有两个:

  • 目前制作表达式模板是否存在原则性障碍具有变换功能 (参见 boost::yap 或下面我的代码)Rust 不可能吗?
  • 如果不是,我怎样才能让它发挥作用?

这是我到目前为止所想到的。

我有一个枚举E代表所有支持的操作。实际上,它需要两个通用参数来表示任何二元运算的左侧和右侧表达式,并且会有称为Add, Mul, Sub等等。我会实现这些特征std::ops::{Add, Mul, Sub}等对于E<U>.

然而,出于演示目的,我们假设我们只有两种变体,Terminal表示包装值的表达式,并且Neg是目前唯一支持的一元运算。

use std::ops::Neg;

enum E<U> {
    Terminal(U),
    Neg(U)
}

impl<U> Neg for E<U> {
    type Output = E<E<U>>;
    fn neg(self) -> Self::Output {
        E::Neg(self)
    }
}

接下来,我实现一个特质Transform这让我可以通过带有闭包的子表达式来遍历表达式。一旦返回,闭包就会停止递归Some(_)。这就是我想出的(代码无法编译):

trait Transform<Arg = Self> {

    fn transform<R,F>(&self, _f: F) -> Option<R>
    where F: FnMut(&Arg) -> Option<R> 
    {
        None
    }
}

impl<U> Transform for E<U> 
where U : Transform<U> + Neg
{
    fn transform<R,F>(&self, mut f: F) -> Option<R>
    where F: FnMut(&Self) -> Option<R>
    {
        // CASE 1/3: Match! return f(self)
        if let Some(v) = f(self) { return Some(v); };

        match self {
            E::Terminal(_) => None, // CASE 2/3: We have reached a leaf-expression, no match!
            E::Neg(x) => {      // CASE 3/3: Recurse and apply operation to result
                x.transform(f).map(|y| -y) // <- error[E0277]: expected a `FnMut<(&U,)>` closure, found `F`
            }
        }
    }
}

这是编译器错误:

error[E0277]: expected a `FnMut<(&U,)>` closure, found `F`
  --> src/main.rs:36:29
   |
36 |                 x.transform(f).map(|y| -y) // <- error[E0277]: expected a `Fn<(&U,)>` closure, found `F`
   |                             ^ expected an `FnMut<(&U,)>` closure, found `F`
   |
help: consider further restricting this bound
   |
28 |     where F: FnMut(&Self) -> Option<R> + for<'r> std::ops::FnMut<(&'r U,)>
   |                                        +++++++++++++++++++++++++++++++++++

这是我的问题 1/2:我想传递一个可以同时工作的闭包Self and on U for E<U>(因此也接受E<E<U>> and E<E<E<U>>>...)。 Rust 中的泛型类型可以这样做吗?或者如果我的方法是错误的,那么正确的方法是什么?在 C++ 中我会使用 SFINAE 或if constexpr.

这是对表达式模板库的一个小测试,看看如何使用它:

fn main() {
    //This is needed, because of the trait bound `U: Transform` for `Transform`
    //Seems like an unnecessary burden on the user...
    impl Transform for i32{}

    // An expression template 
    let y = E::Neg(E::Neg(E::Neg(E::Terminal(42))));

    // A transform that counts the number of nestings
    let mut count = 0;
    y.transform(|x| {
        match x {
            E::Neg(_) => {
                count+=1;
                None
            }
            _ => Some(()) // must return something. It doesn't matter what here.
        }
    });
    assert_eq!(count, 3);
    
    // a transform that replaces the terminal in y with E::Terminal(5)
    let expr = y.transform(|x| {
       match x {
           E::Terminal(_) => Some(E::Terminal(5)),
           _ => None
       } 
    }).unwrap();
    
    // a transform that evaluates the expression
    // (note: should be provided as method for E<U>)
    let result = expr.transform(|x| {
        match *x {
            E::Terminal(v) => Some(v),
            _ => None
        }
    }).unwrap();
    assert_eq!(result, -5);
}

My 第2/2期不是一个交易破坏者,但我想知道是否有某种方法可以使代码在没有这一行的情况下工作:

impl Transform for u32{}

我认为必须这样做对于这样一个库的用户来说是很麻烦的。问题是,我有特质限制U: Transform关于实施Transform for E<U>。我感觉不稳定的专业化功能可能会有所帮助,但如果这可以用稳定的 Rust 来完成,那就太棒了。

这里是.

Edit:

如果其他人遇到了这个问题,这里有一个它实现了已接受答案的解决方案。它还清理了上面代码中的一些小错误。


这看起来像一个访客模式 https://en.wikipedia.org/wiki/Visitor_pattern to me.

解决方案here https://rust-unofficial.github.io/patterns/patterns/behavioural/visitor.html看起来与您想要做的类似。

与您的尝试不同的是,U 类型放置在堆上Box<U>(考虑到unique_ptr<U>在C++中),这使得整个E的大小固定(独立于具体的U)。

除了让编译器愿意统一 F 和 Transform 的类型以便它可以同时使用 E 和 U 之外,F 的特征参数可能还需要装箱Box<dyn Transform>(这大致类似于标记transform作为 C++ 中的虚拟方法,能够通过“智能”指针调用它,该指针知道如何在运行时找到特定的 impl)。拆开包装后x: U使用匹配,应该可以制作一个Box::<dyn Transform>::new(x)从那时起U : Transform。这样F就接受了。

如果没有 Box,我认为唯一的其他解决方案是使用宏,并显式为每种类型生成方法。

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

Rust 中的表达式模板实现类似于 boost::yap 的相关文章

随机推荐

  • Chromium 中的 MP4 编解码器支持

    我们已将 Chromium 嵌入式框架集成到我们的 Windows 游戏中 以允许我们从应用程序内渲染网页 并且一切正常 除了 MP4 视频 据我所知 由于许可问题 Chromium 不包含此编解码器 但任何人都可以提供有关我们如何添加支持
  • JQGrid 列自定义..在运行时添加列

    我是 J Query 的新手 正在尝试一些示例http www trirand com blog jqgrid jqgrid html http www trirand com blog jqgrid jqgrid html我看到列名是用
  • MATLAB问题:在图块中引用变量的值[重复]

    这个问题在这里已经有答案了 可能的重复 matlab 绘图标题中的变量 https stackoverflow com questions 5629458 matlab variable in plot title 我想在图中引用 m 文件
  • 通过 RDP 使用 WPF 的 Direct2d

    我正在开发一个 C 应用程序 它使用 SharpDx 通过 Direct2d 渲染地图 该地图与 D3DImage 一起显示在 WPF 主机上 在本地计算机上 一切正常 但当我尝试通过远程桌面连接时 D3DImage 会丢失其后备缓冲区 并
  • Android蓝牙java.io.IOException:bt套接字已关闭,读取返回:-1

    我正在尝试编写一个代码 仅连接到运行 Android 5 0 KitKat 的设备上的 目前 唯一配对的设备 无论我尝试了多少方法 我仍然会收到此错误 这是我尝试过的最后一个代码 它似乎完成了我看到人们报告为成功的所有事情 有人能指出我做错
  • 在 MongoDB 和 Apache Solr 之间同步数据的简单方法

    我最近开始使用 MongoDB 和 Apache Solr 我使用 MongoDB 作为数据存储 并且希望 Apache Solr 为我的数据创建索引 以实现应用程序中的搜索功能 经过一些研究 我发现 基本上有两种方法可以在 MongoDB
  • 为什么 Angular (1.5) 组件总是有一个隔离的范围?

    我正在构建一个 Angular 库 它提供了一堆组件 这些组件应该可以更轻松地在特定 API 之上构建 SPA 应用程序 对于某些组件 我们使用多槽嵌入功能 AngularJS 1 5 版本中引入了多槽嵌入和组件 我真的很喜欢这两个功能 但
  • 单击在 Whatsapp 或通过 PhoneGap 的任何社交应用程序中共享的链接时如何打开应用程序?

    1 当我通过phonegap插件分享链接到whatsapp时 当用户点击链接时 它应该检查是否安装了特定的应用程序 如果是 请在应用程序中打开该链接 或者打开显示下载应用程序的 Play 商店链接 您所描述的场景 其中设备拦截 HTTP 协
  • 有什么方法可以让 css 选择除第一页之外的所有内容吗?

    我刚刚找到了 CSS page指令 并将其与 first将 CSS 应用到 html 打印的第一页 有没有办法反其道而行之 将 CSS 应用于所有页面except首先 使用 CSS3 not https developer mozilla
  • 如何将 csv 文件读取为键值对的映射

    我的 csv 文件中有数据 例如 value key A Name B Name C Name 24 Age 25 Age 20 Age M Gender F Gender 我想解析它以生成以下地图 Map Name gt List A B
  • 什么是更好的?子查询或内连接十个表?

    一个旧系统已抵达我们的办公室进行一些更改和修复 但它也存在性能问题 我们并不确切知道这种缓慢的根源是什么 当我们重构旧代码时 我们发现了几个具有以下模式的 sql 查询 出于示例目的 简化了查询 SELECT SELECT X FROM A
  • 重定向时重新初始化类

    我目前在http example com parentdir module 2 此 URL 实际上加载 Module js 类 如以下路由所示
  • MIUI 权限被拒绝活动 KeyguardLocked

    当应用程序处于后台且屏幕被锁定时 我无法启动活动 没有异常或警告 只是不调用 onCreate 我一直在与这个问题作斗争 我想我终于找到了它的根源 日志中有一行 D com android server am ExtraActivityMa
  • http和https在编程中有什么区别[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我只知道 s 代表 安全 用户永远不
  • 如何访问其他类中的控件

    我正在学习 C 我需要的是来自其他类的表单的访问控制 相同的命名空间 我知道这里有很多关于这个主题的帖子 但没有找到 傻瓜 的完整解决方案 所以我在这里写下我的想法 请告诉我 这是正确的方法吗 背景 我的应用程序中有一些 调试 表单 我需要
  • VBA在下一个空白行正确输入用户表单数据

    创建了一个用户表单 添加了一个文本框和一个组合框 添加了提交按钮 单击 提交 后 会将数据添加到电子表格中 据我所知和我所读到的 这是错误的 ActiveCell Value TextBox3 Text ActiveCell Offset
  • 在 Ansible 中使用模板模块附加文件

    所以我有一个 ansible 剧本 它使用 Jinja2 模板来创建日志文件 每次我运行剧本时 它都会从customers yml中提取客户信息 并将完整的模板输出到 stunnel conf 文件中 该模板工作正常 但我试图找到一种方法来
  • 在 Vulkan 中,图形队列系列与当前队列系列分离是否有益?

    据我所知 队列系列可能支持呈现到屏幕但不支持图形 假设我有一个同时支持图形和呈现的队列系列 以及另一个仅支持呈现的队列系列 我应该为两个进程使用第一个队列系列 还是应该将第一个队列系列委托给图形 将后者委托给呈现 或者这两种方法之间没有明显
  • VLC 媒体播放器有 C# 界面吗? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 是否可以使用 C 控制台应用程序中的包装器从 VLC 播放中当前播放的文件中读取曲目统计信息 时间 标
  • Rust 中的表达式模板实现类似于 boost::yap

    我正在尝试自学 Rust 作为一个具有挑战性的学习项目 我想复制 C 表达式模板库的设计模式提升 雅普 https www boost org doc libs 1 74 0 doc html yap html 我不需要一个完整的实现 我只