如何通过条件编译更改函数的限定符?
How do I change a function's qualifiers via conditional compilation?
我有一个函数可以实现为 const
:
#![feature(const_fn)]
// My crate would have:
const fn very_complicated_logic(a: u8, b: u8) -> u8 {
a * b
}
// The caller would have:
const ANSWER: u8 = very_complicated_logic(1, 2);
fn main() {}
我想在无法定义此类函数的情况下继续支持稳定的 Rust。这些稳定的消费者将无法在 const
或 static
中使用该函数,但应该能够在其他上下文中使用该函数:
// My crate would have:
fn very_complicated_logic(a: u8, b: u8) -> u8 {
a * b
}
// The caller would have:
fn main() {
let answer: u8 = very_complicated_logic(1, 2);
}
我如何有条件地编译我的代码,以便我的箱子的冒险用户可以启用 const fn
支持,稳定的用户仍然可以使用我的代码,而且我不必将每个函数都写两次?
同样的问题应该适用于函数的其他修饰符,但我不确定这些修饰符会根据某些条件发生变化的具体情况:
default
unsafe
extern
pub
(和其他可见性修饰符)
宏来拯救!
#![cfg_attr(feature = "const-fn", feature(const_fn))]
#[cfg(not(feature = "const-fn"))]
macro_rules! maybe_const_fn {
($($tokens:tt)*) => {
$($tokens)*
};
}
#[cfg(feature = "const-fn")]
macro_rules! maybe_const_fn {
($(#[$($meta:meta)*])* $vis:vis $ident:ident $($tokens:tt)*) => {
$(#[$($meta)*])* $vis const $ident $($tokens)*
};
}
maybe_const_fn! {
#[allow(unused)] // for demonstration purposes
pub fn very_complicated_logic(a: u8, b: u8) -> u8 {
internally_complicated_logic(a, b)
}
}
maybe_const_fn! {
fn internally_complicated_logic(a: u8, b: u8) -> u8 {
a * b
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "const-fn")]
#[test]
fn use_in_const() {
const ANSWER: u8 = very_complicated_logic(1, 2);
drop(ANSWER);
}
#[test]
fn use_in_variable() {
let answer: u8 = very_complicated_logic(1, 2);
drop(answer);
}
}
连同 Cargo.toml
中的这个:
[features]
const-fn = []
由于宏只能扩展为完整的语法片段(即宏不能简单地扩展为 const
),我们必须将整个函数包装在宏中并保留其中的某些部分未解析,以便我们可以在合适的地方注入const
。然后,解析器可以将整个事物解析为函数定义。
属性和可见性限定符需要特殊处理,因为它们必须出现在 const
之前。我正在使用 vis
匹配器 (available since Rust 1.30.0) 来简化宏的实现。
我有一个函数可以实现为 const
:
#![feature(const_fn)]
// My crate would have:
const fn very_complicated_logic(a: u8, b: u8) -> u8 {
a * b
}
// The caller would have:
const ANSWER: u8 = very_complicated_logic(1, 2);
fn main() {}
我想在无法定义此类函数的情况下继续支持稳定的 Rust。这些稳定的消费者将无法在 const
或 static
中使用该函数,但应该能够在其他上下文中使用该函数:
// My crate would have:
fn very_complicated_logic(a: u8, b: u8) -> u8 {
a * b
}
// The caller would have:
fn main() {
let answer: u8 = very_complicated_logic(1, 2);
}
我如何有条件地编译我的代码,以便我的箱子的冒险用户可以启用 const fn
支持,稳定的用户仍然可以使用我的代码,而且我不必将每个函数都写两次?
同样的问题应该适用于函数的其他修饰符,但我不确定这些修饰符会根据某些条件发生变化的具体情况:
default
unsafe
extern
pub
(和其他可见性修饰符)
宏来拯救!
#![cfg_attr(feature = "const-fn", feature(const_fn))]
#[cfg(not(feature = "const-fn"))]
macro_rules! maybe_const_fn {
($($tokens:tt)*) => {
$($tokens)*
};
}
#[cfg(feature = "const-fn")]
macro_rules! maybe_const_fn {
($(#[$($meta:meta)*])* $vis:vis $ident:ident $($tokens:tt)*) => {
$(#[$($meta)*])* $vis const $ident $($tokens)*
};
}
maybe_const_fn! {
#[allow(unused)] // for demonstration purposes
pub fn very_complicated_logic(a: u8, b: u8) -> u8 {
internally_complicated_logic(a, b)
}
}
maybe_const_fn! {
fn internally_complicated_logic(a: u8, b: u8) -> u8 {
a * b
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "const-fn")]
#[test]
fn use_in_const() {
const ANSWER: u8 = very_complicated_logic(1, 2);
drop(ANSWER);
}
#[test]
fn use_in_variable() {
let answer: u8 = very_complicated_logic(1, 2);
drop(answer);
}
}
连同 Cargo.toml
中的这个:
[features]
const-fn = []
由于宏只能扩展为完整的语法片段(即宏不能简单地扩展为 const
),我们必须将整个函数包装在宏中并保留其中的某些部分未解析,以便我们可以在合适的地方注入const
。然后,解析器可以将整个事物解析为函数定义。
属性和可见性限定符需要特殊处理,因为它们必须出现在 const
之前。我正在使用 vis
匹配器 (available since Rust 1.30.0) 来简化宏的实现。