我有一个程序属性宏,给定一个函数对每个二进制表达式进行操作,例如let a = b + c;并根据它返回另一个表达式。根据类型进行操作,需要知道、、+的类型。abc
有没有办法在编译时获取变量的推断类型?
(就像 rust-analysisr 可能会显示推断的类型一样,我可以在宏中获取这些类型吗?)
为了在 Rust 游乐场中更简洁地说明我的方法,我们可以使用声明性宏来调用给定变量上的函数,该函数的细节基于给定变量的类型。
我在 Rust 游乐场中最接近我想要的功能:
macro_rules! SomeMacro {
($x:expr) => {{
$x.some_function(3.)
}};
}
trait SomeTrait {
fn some_function(&self,x:f32) -> f32;
}
impl SomeTrait for u32 {
fn some_function(&self,x:f32) -> f32 {
x * 3.
}
}
fn main() {
let a = 3u32;
let b = SomeMacro!(a);
assert_eq!(b,9.);
}
Run Code Online (Sandbox Code Playgroud)
我怎样才能让事情像这样工作:
fn some_function<u32>(x:f32) -> f32 {
3. * x
}
fn some_function<u32,i8,f32>(x:f32) -> f32 {
3. * x
}
fn main() {
let a = 3u32;
let b = <type_of<a>()>::some_function(3.);
assert_eq!(b,9.);
let c = 5i8;
let d = <type_of<a>(),type_of<b>(),type_of<c>()>::some_function(2.);
assert_eq!(c,6.);
}
Run Code Online (Sandbox Code Playgroud)
lib.rsextern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_attribute]
pub fn my_attribute_macro(_attr: TokenStream, item: TokenStream) -> TokenStream {
let ast = syn::parse_macro_input!(item as syn::Item);
// eprintln!("{:#?}",ast);
// Checks item is function.
let mut function = match ast {
syn::Item::Fn(func) => func,
_ => panic!("Only `fn` items are supported."),
};
let block = &mut function.block;
// Updates statements
let statements = block.stmts
.iter()
.filter_map(|statement| update_statements(statement))
.collect::<Vec<_>>();
block.stmts = statements;
let new = quote::quote! { #function };
TokenStream::from(new)
}
fn update_statements(stmt: &syn::Stmt) -> Option<syn::Stmt> {
let local = match stmt {
syn::Stmt::Local(local) => local,
_ => return Some(stmt.clone())
};
let init = &local.init;
let bin_expr = match *init.as_ref().unwrap().1 {
syn::Expr::Binary(ref bin) => bin,
_ => return Some(stmt.clone())
};
eprintln!("looking at: {:#?}",stmt);
//
None
}
Run Code Online (Sandbox Code Playgroud)
main.rsuse macro_test::*;
// Goals:
// - Map from `x` being equal to `a+b` to `x` being equal to `a*b` based off `x` being `f32`.
// - Map from `y` being equal to `c+d` to `y` being equal to `c/d` based off `y` being `u32`.
#[my_attribute_macro]
fn my_function(a: f32, b: f32, c: u32, d: u32) {
let x = a + b;
let y = c + d;
}
fn main() {
}
Run Code Online (Sandbox Code Playgroud)
其中一张照片看起来像(来自cargo expand --bin macro-test):
looking at: Local(
Local {
attrs: [],
let_token: Let,
pat: Ident(
PatIdent {
attrs: [],
by_ref: None,
mutability: None,
ident: Ident {
ident: "y",
span: #0 bytes(316..317),
},
subpat: None,
},
),
init: Some(
(
Eq,
Binary(
ExprBinary {
attrs: [],
left: Path(
ExprPath {
attrs: [],
qself: None,
path: Path {
leading_colon: None,
segments: [
PathSegment {
ident: Ident {
ident: "c",
span: #0 bytes(320..321),
},
arguments: None,
},
],
},
},
),
op: Add(
Add,
),
right: Path(
ExprPath {
attrs: [],
qself: None,
path: Path {
leading_colon: None,
segments: [
PathSegment {
ident: Ident {
ident: "d",
span: #0 bytes(324..325),
},
arguments: None,
},
],
},
},
),
},
),
),
),
semi_token: Semi,
},
)
Run Code Online (Sandbox Code Playgroud)
在处理宏之前,编译器不会决定类型。在许多情况下,宏可以更改推断的类型。这就是为什么可以写这样的东西:
let mut xs = vec![];
xs.push(1);
Run Code Online (Sandbox Code Playgroud)
类型推断可以返回并将正确的类型分配给xs. vec!如果必须在扩展之前知道类型,这是不可能的。您需要以另一种方式解决这个问题。请参阅@PossibleAShrub's 答案了解一种方法。
正如《Rust 编程语言:宏》所指出的(强调是添加的):
此外,宏在编译器解释代码含义之前会被扩展,因此宏可以在给定类型上实现特征。
您可能想从论坛中探索Rust 的本机差分编程支持讨论,特别是链接的后续线程。
OP 已更新他们的帖子澄清他们的问题,此添加是对此的回应。
看来您可能想要实现新的类型模式。在您的评论中,您提到您想要更改用特定过程宏修饰的函数上的标准运算符( +、-、*、 ...)的行为。/
新类型模式如下所示:
struct Differentiable(f32);
Run Code Online (Sandbox Code Playgroud)
注意:我们甚至可以在这里使用泛型,这样我们的新类型就可以超过
f32,u32,u8...
我们现在可以在新类型之上实现我们想要的运算符。
impl Add for Differentiable {
type Output = Self;
fn add(self, other: Self) -> Self {
Self(self.0 * other.0)
}
}
Run Code Online (Sandbox Code Playgroud)
这给了我们以下行为:
fn main() {
let x = Differentiable(5.0);
let y = Differentiable(2.0);
assert_eq!(x + y, 10);
}
Run Code Online (Sandbox Code Playgroud)
此外,我们可以在类型上实施Into和 ,From使其使用起来更符合人体工程学。我们还可以通过实现 中的特征来实现其他运算符std::ops。
现在,您提到想要将其实现为函数上的过程宏。我强烈建议否则。我认为这样的功能将:
看:
就我个人而言,我不认为提供这样一个宏有什么价值,也不知道如何以“感觉”正确的方式实现它。尽管如此,这是可能的:
只需从此 ast 进行转换:
fn my_function(a: f32, b: f32, c: u32, d: u32) {
let x = a + b;
let y = c + d;
}
Run Code Online (Sandbox Code Playgroud)
到:
fn my_function(a: f32, b: f32, c: u32, d: u32) {
fn inner(
a: Differentiable,
b: Differentiable,
c: Differentiable,
d: Differentiable,
) {
let x = a + b;
let y = c + d;
}
inner(
Differentiable(a),
Differentiable(b),
Differentiable(c),
Differentiable(d),
)
}
Run Code Online (Sandbox Code Playgroud)
进一步阅读:
我不确定知道宏内部表达式的类型对您有什么用。Rust 的类型推断系统应该足够强大,可以处理大多数类型解析的情况。
例如,我将使用泛型和特征边界来实现上面的代码片段:
fn main() {
let x = Differentiable(5.0);
let y = Differentiable(2.0);
assert_eq!(x + y, 10);
}
Run Code Online (Sandbox Code Playgroud)
ARust 会自动找到和的正确值B。如果不能,由于我们的特征限制,rustc 将输出有用的错误消息:
fn main() {
let a = "Hello, world!";
let b = 56;
add(a, b);
}
Run Code Online (Sandbox Code Playgroud)
结果是:
error[E0277]: the trait bound `{integer}: From<&str>` is not satisfied
--> src/main.rs:10:12
|
10 | add(a, b);
| --- ^ the trait `From<&str>` is not implemented for `{integer}`
| |
| required by a bound introduced by this call
|
= help: the following implementations were found:
<Arc<B> as From<Cow<'a, B>>>
<Arc<CStr> as From<&CStr>>
<Arc<CStr> as From<CString>>
<Arc<OsStr> as From<&OsStr>>
and 329 others
= note: required because of the requirements on the impl of `Into<{integer}>` for `&str`
note: required by a bound in `add`
--> src/main.rs:3:11
|
3 | fn add<A: Into<B>, B: Add>(a: A, b: B) -> <B as Add>::Output {
| ^^^^^^^ required by this bound in `add`
For more information about this error, try `rustc --explain E0277`.
Run Code Online (Sandbox Code Playgroud)
这里 Rust 无法解析 的类型a来满足我们对函数的限制add。
现在,这可以通过宏来实现,无需额外的工作:
macro_rules! some_macro {
($x:expr) => (add($x, 3.0))
}
fn main() {
let x = 42u32;
let y = some_macro!(x);
assert_eq!(y, 45.0);
}
Run Code Online (Sandbox Code Playgroud)
进一步阅读:
| 归档时间: |
|
| 查看次数: |
3096 次 |
| 最近记录: |