当前位置: 首页 > 知识库问答 >
问题:

宏是否可以接受常量表达式并“内联”它以生成有效的文字模式?

史劲
2023-03-14

我想根据一个域匹配一个复杂常数。如果在另一个模式上选择一个常量,则不允许在另一个模式上选择一个常量。

是否可以编写一个宏,将常量表达式的结果在编译时“内联”以使生成的代码是有效的LiteralPattern

考虑以下工作代码

pub struct Instruction {
    code: u8,
    width: usize,
}

pub const CMP_REG_U8: Instruction = Instruction { code: 3, width: 3 };
pub const CMP_REG_U32: Instruction = Instruction { code: 4, width: 6 };
pub const INVALID: Instruction = Instruction { code: 0, width: 0 };

fn main() {
    let opcode = 3u8;
    
    let inst = match opcode {
        3 => CMP_REG_U8,
        4 => CMP_REG_U32,
        _ => INVALID,
    };

    println!("inst.code: {} inst.width: {}", inst.code, inst.width);
}

我希望能够基本上写下:

let inst = match opcode {
    CMP_REG_U8.code => CMP_REG_U8,
    CMP_REG_U32.code => CMP_REG_U32,
    _ => INVALID,
};

这样,我就不必在我的比赛陈述中输入神奇的数字,也不必重复第二次。

我意识到我可以使用警卫:

let inst = match opcode {
    x if x == CMP_REG_U8.code => CMP_REG_U8,
    x if x == CMP_REG_U32.code => CMP_REG_U32,
    _ => INVALID,
};

...但这似乎有点冗长,我关心的是如何说服编译器给我一个跳转表实现。(想象一下,与将值硬编码为文本相比,我们不只是返回指令,而是希望执行一段代码,并能够引用相关数据,如inst.width

所以底线是,有没有可能用一个“文字化”常量的宏来解决这个问题?

let inst = match opcode {
    as_literal!(CMP_REG_U8.code) => exec_op(CMP_REG_U8),
    as_literal!(CMP_REG_U32.code) => exec_op(CMP_REG_U32),
    _ => INVALID,
};

这样,匹配臂都将是硬编码的整数,这将使编译器非常容易内联exec_op所做的任何事情,将宽度等识别为该上下文中的常量,并执行计算的goto或其他任何操作。

我听说Rust中的宏只允许你做你本可以自己打字的事情。然而,我当然可以输入3,而不是CMP_REG_U8。代码——问题是锈能不能帮我。它需要在编译时解决这个问题,以使生成的模式在匹配中有效。

共有1个答案

吴俊风
2023-03-14

可以使用一个宏生成另一个宏:

macro_rules! define_consts {
    ($(
        $vis: vis const $name: ident : Instruction = Instruction { code: $code: literal, width: $width: literal };
    )*) => {
        $(
            $vis const $name: Instruction = Instruction { code: $code, width: $width };
        )*
        
        macro_rules! as_literal {
            $(($name . code) => { $code });*
        }
    }
}

用你的常量定义来称呼它:

define_consts! {
    pub const CMP_REG_U8: Instruction = Instruction { code: 3, width: 3 };
    pub const CMP_REG_U32: Instruction = Instruction { code: 4, width: 6 };
    pub const INVALID: Instruction = Instruction { code: 0, width: 0 };
}

将生成写入的常量,以及一个宏as_literal!,它可以执行您想要的操作:

fn exec_op(inst: Instruction) -> Instruction {
    inst
}

fn main() {
    let opcode = 3u8;
    
    let inst = match opcode {
        as_literal!(CMP_REG_U8.code) => exec_op(CMP_REG_U8),
        as_literal!(CMP_REG_U32.code) => exec_op(CMP_REG_U32),
        _ => INVALID,
    };

    println!("inst.code: {} inst.width: {}", inst.code, inst.width); // 3
}

不过,我不确定这有多有价值!鉴于模式都是文字,使用if...否则if...而不是匹配最终可能会生成相同的程序集。

 类似资料:
  • 我想编写一个由文件路径参数化的宏。在编译时,它读取该文件并根据其内容发出Rust代码。这可能吗? 我发现但不清楚我如何在编译时使用它的结果,而不是将内容按原样拼接到生成的代码中。

  • 问题内容: 是否可以创建内联伪样式? 例如,我可以做以下事情吗? 这背后的原因是我正在开发一个创建UI元素的.NET库。我想生成无需设置外部样式表即可设置其悬停状态的HTML元素。 问题答案: 不幸的是,不能,您不能使用内联CSS来实现悬停效果。 此问题的(较差)解决方法是让控件在呈现时呈现样式块。例如,您的控件可以呈现为: 如果您可以强迫用户在页面顶部放置“样式控件”,则可以在其中渲染所有自定义

  • 问题内容: 有谁知道我们可以通过XML工具从XML模式生成JSON模式或通过Java从JSON模式生成XML模式的工具或方法? 问题答案: 它不是很优雅,但是jackson可以从java类生成json模式。因此,您可以使用xml模式,并使用jaxb注释从中生成java类,然后从其中生成json模式,因为jackson支持jaxb注释。

  • 我有一个JS项目,它使用aws服务作为后端(Cognito、AppSync、S3等)。 我使用AWS Amplify访问这些服务,但不使用Amplify CLI;所有aws服务都是手动配置的。 关于AppSync,我有一个小脚本,它读取模式(从APPESNC控制台手动下载),然后生成TypeScript操作和操作类型文件(使用放大器-图形ql-文档-生成器和放大器-图形ql-类型-生成器)。 有没

  • valgrind快速启动页面提到: 这将我引向这样一个问题:是否有充分的理由不在生产中从程序中消除这样的错误?发布包含此类错误的程序有什么好处吗?如果有,开发人员如何保护它的安全,尽管据我所知,包含此类错误的程序可以不可预测地运行,并且无法对其行为做出一般假设?如果是这样,你能提供真实世界的例子,说明程序运行时有这些错误比没有错误更好吗?