『科技排头』在Rust代码中编写Python是种怎样的体验?( 二 )


macro_rules!python{($($code:tt)*)=>{run_python(stringify!($($code)*));}}结果如下:
$cargorCompilingscratchpadv0.1.0Finisheddev[unoptimized+debuginfo]target(s)in0.32sRunning`target/debug/scratchpad`Hello......World!如愿以偿得到了期望结果!
但是 , 如果我们有不止一行的Python代码会怎样?
fnmain{println!(''Hello...'');python!{print(''...World!'')print(''Bye.'')}}$cargorCompilingscratchpadv0.1.0Finisheddev[unoptimized+debuginfo]target(s)in0.31sRunning`target/debug/scratchpad`Hello...thread'main'panickedat'called`Result::unwrap`onan`Err`value:PyErr{type:Py(0x7f1c0a5649a0,PhantomData)}', src/main.rs:9:5note:runwith`RUST_BACKTRACE=1`environmentvariabletodisplayabacktrace很不幸 , 我们失败了 。
为了进行调试 , 我们需要正确输出PyErr , 并显示我们传递给Python::run的确切Python代码:
fnrun_python(code:&str){println!(''-----'');println!(''{}'',code);println!(''-----'');letpy=pyo3::Python::acquire_gil;ifletErr(e)=py.python.run(code,None,None){e.print(py.python);}}$cargorCompilingscratchpadv0.1.0Finisheddev[unoptimized+debuginfo]target(s)in0.27sRunning`target/debug/scratchpad`Hello...-----print(''...World!'')print(''Bye.'')-----File''<string>'',line1print(''...World!'')print(''Bye.'')^SyntaxError:invalidsyntax很显然 , 两行Python代码落在同一行 , 在Python中这是无效的语法 。
现在我们遇到了必须克服的最大问题:stringify!把空白符搞乱了.
『科技排头』在Rust代码中编写Python是种怎样的体验?
文章图片
空白符和符号
让我们仔细研究一下stringify!:
fnmain{println!(''{}'',stringify!(a123bcx(y+z)//comment...));}$cargorCompilingscratchpadv0.1.0Finisheddev[unoptimized+debuginfo]target(s)in0.21sRunning`target/debug/scratchpad`a123bcx(y+z)...它不仅删除了所有不必要的空格 , 还删除了注释 。 因为它的工作原理是处理单词(token) , 不再是源代码里面的:a , 123 , b等 。
【『科技排头』在Rust代码中编写Python是种怎样的体验?】Rustc编译器做的第一件事就是将源代码分为单词 , 这使得解析后的工作更容易进行 , 不必处理诸如1 , 2 , 3 , 这样的个别字符 , 只需处理诸如“integerliteral123”这样的单词 。 另外 , 空白和注释在分词之后就消失了 , 因为它们对编译器来说没有意义 。
stringify!是一种将一串单词转换回字符串的方法 , 但它是基于“最佳效果”的:它将单词转换回文本 , 并且仅在需要时才在单词周围插入空格(以避免将b、c转换为bc) 。
所以这是一个死胡同 。 Rustc不小心把宝贵的空白符丢掉了 , 但这在Python中非常重要 。
我们可以尝试猜测一下哪些代码的空格必须用换行符代替 , 但是缩进肯定会成为一个问题:
fnmain{leta=stringify!(ifFalse:xy);letb=stringify!(ifFalse:xy);dbg!(a);dbg!(b);dbg!(a==b);}$cargorCompilingscratchpadv0.1.0Finisheddev[unoptimized+debuginfo]target(s)in0.20sRunning`target/debug/scratchpad`[ src/main.rs:12]a=''ifFalse:xy''[ src/main.rs:13]b=''ifFalse:xy''[ src/main.rs:14]a==b=true这两个Python代码片段有不同的含义 , 但是stringify!给了我们相同的结果 。
在放弃之前 , 让我们尝试一下其他类型的宏 。
『科技排头』在Rust代码中编写Python是种怎样的体验?
文章图片
过程宏
Rust的过程宏是定义宏的另一种方法 。 尽管macro_rules!只能定义“函数样式的宏”(带有!标记的宏) , 过程宏也可以定义自定义派生宏(例如#[derive(Stuff)])和属性宏(例如#[stuff]) 。