gpt4 book ai didi

rust - 如何根据编译功能标志为枚举添加生命周期

转载 作者:行者123 更新时间:2023-12-03 11:24:50 24 4
gpt4 key购买 nike

我目前正在尝试实现一个协议(protocol)库,其中协议(protocol)的许多部分都位于功能标志后面。为此,我想知道 Rust 中是否有符合人体工程学的方式让我的枚举中的一个具有生命周期参数,具体取决于某些功能标志。
在解析方面,我有一个名为 Action 的已解析项目看起来像这样:

enum Action<'a> {
Nop(Nop),
WriteFileData(WriteFileData<'a>),
...
}

struct Nop {
pub group: bool,
pub response: bool,
}

struct WriteFileData<'a> {
...
pub data: &'[u8], // Not exactly the final code, but equivalent
}
到目前为止,一切都很好。
但是然后在我的库中,我希望这些操作类型中的每一个都位于功能标志后面:
enum Action<'a> {
#[cfg(feature = "decode_nop")]
Nop(Nop),
#[cfg(feature = "decode_write_file_data")]
WriteFileData(WriteFileData<'a>),
...
}
此时,如果您仅选择解码没有生命周期的操作类型的功能(例如仅 decode_nop),您最终会编译:
enum Action<'a> {
Nop(Nop),
}
哪个不编译,因为枚举声明中有一个无用的生命周期。
我首先发现了有条件的生命周期的有希望的线索:
enum Action<#[cfg(feature = "decode_action_lifetime")] 'a> {
...
}

impl<#[cfg(feature = "decode_action_lifetime")] 'a> Action<#[cfg(feature = "decode_action_lifetime")] 'a> {
...
}
不幸的是,这不会编译,因为:
error: expected one of `>`, const, identifier, lifetime, or type, found `#`
--> src/v1_2/action/mod.rs:123:63
|
123 | impl<#[cfg(feature = "decode_action_lifetime")] 'a> Action<#[cfg(feature = "decode_action_lifetime")]'a> {
| ^ expected one of `>`, const, identifier, lifetime, or type
由于这是一个语法错误,我什至不确定我在这里尝试做的事情在 Rust 中是否合法。
然后我想出了两种解决问题的方法:
  • 在适当的功能标志组合后面使用生命周期和非生命周期变体复制操作定义。
    #[cfg(feature = "decode_with_lifetime")]
    enum Action<'a> {
    #[cfg(feature = "decode_nop")]
    Nop(Nop),
    #[cfg(feature = "decode_write_file_data")]
    WriteFileData(WriteFileData<'a>),
    ...
    }

    #[cfg(not(feature = "decode_with_lifetime"))]
    enum Action {
    #[cfg(feature = "decode_nop")]
    Nop(Nop),
    ...
    }
    问题是它需要复制所有引用该类型的代码:
  • 实现 block 。
  • 使用此类型的其他功能。
  • 在尝试支持相同功能标志时将使用此库的外部代码。

  • 因此,我显然不喜欢该解决方案,因为它对我的 crate 以及使用该 crate 的代码的维护成本很高。
  • 使用 Nop<'a> 将生命周期参数添加到所有没有的子项( PhantomData<&'a ()> )在他们的结构中标记。
    struct Nop<'a> {
    pub group: bool,
    pub response: bool,
    pub phantom: PhantomData<&'a ()>,
    }
    但是,那些非生命周期要求结构的公开声明,我认为它是我的 API 的一部分(所有字段均可访问,以便可以直接构建它们),现在突然包含一个不方便且不直观的幻像字段。
    因此,如果我希望我的 API 对用户来说更清晰,这可能意味着我必须为每个结构添加构建器函数,这样用户就不必关心我的库实现细节(幻像字段)。
    另外,Rust 在其函数中不支持任何类型的命名参数(据我所知),这使得具有大量字段(> 4 个字段)的结构的构建器难以阅读且易于使用错误。解决方案可能是创建一个与第一个没有幻像参数的结构完全相同的参数类型,但这意味着添加更多代码。
    struct Nop<'a> {
    pub group: bool,
    pub response: bool,
    pub phantom: PhantomData<&'a ()>,
    }

    struct NopBuilderParam {
    pub group: bool,
    pub response: bool,
    }

    impl<'a> Nop<'a> {
    pub new(group: bool, response: bool) -> Self {
    Self {
    group,
    response,
    phantom: phantomData,
    }
    }
    }
    而且,虽然它不应该是一个真正的问题,但将这个生命周期约束添加到一个完全拥有的结构中(这意味着对用户代码的额外约束)让我有点困扰。但我想我可以忍受那个。

  • 综上所述:
    我目前正在使用解决方案 2。但我非常想知道是否有另一种方法可以做我想做的事情。

    最佳答案

    根据特性更改类型的类型或生命周期参数的数量通常不是一个好主意。如果您在同一个项目中有多个 crate 使用不同的功能,则 any 激活的所有功能都将被激活。这将是脆弱的,因为一个 crate 可以启用一项功能,这会导致另一个 crate 中的编译错误。
    根据特性更改类型的唯一好时机是所有 crate 的条件始终相同,例如目标架构或字长。
    您的解决方案 PhantomData似乎不是一个很好的妥协。污染枚举使用的所有类型非常麻烦。一些替代方案:
    添加一个额外的枚举变量:

    enum Action<'a> {
    Nop(Nop),
    ...
    _NotUsed<PhantomData<&'a ()>>,
    }
    这实际上不会那么糟糕 #[non_exhaustive] (假设这是有道理的),因为它会强制用户匹配 _并且不必看到丑陋的额外变体。
    避免在此枚举之外进一步扩展生命周期的另一种方法是将其添加到每个变体中,如下所示:
    enum Action<'a> {
    Nop(Nop, PhantomData<&'a ()>),
    WriteFileData(WriteFileData<'a>),
    ...
    }

    完全值得质疑使用功能的想法。如果其中之一为真,则特征门可能很有值(value):
  • 您可以在不使用该功能时减少依赖项的数量
  • 不使用该功能时,您可以显着减少编译时间
  • 你真的很关心二进制大小,这真的很重要
  • 存在真正的兼容性差异,例如操作系统和目标架构,或分配器的可用性。

  • 如果这些都不正确,那么您可能只是为自己和您的用户增加了维护开销,而收效甚微。

    关于rust - 如何根据编译功能标志为枚举添加生命周期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65652212/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com