英文:
How does `tokio::pin` change the type of variable?
问题
将 s
的类型从 alloc::string::String
更改为 core::pin::Pin<&mut alloc::string::String>
是通过调用 tokio::pin!
宏实现的。在宏的作用下,对 s
进行了 Pin
包装,改变了其类型。在宏内部,可能使用了一些特殊的技术或方法来实现这种类型的转换。
英文:
use std::pin::Pin;
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>())
}
fn test(s: String) {}
fn test2(s: Pin<&mut String>) {}
fn main() {
let s = String::from("abc");
print_type_of(&s);
tokio::pin!(s);
print_type_of(&s);
// test(s); error!
test2(s);
}
The question is not about why what Pin
is or why Pin
is needed.
The type of s
changes from alloc::string::String
to core::pin::Pin<&mut alloc::string::String>
after calling tokio::pin!
. How can this happen? Changing the type of the outside variable inside a macro surprises me.
答案1
得分: 4
宏比函数更强大。它们在编译时进行评估,并在被调用的相同作用域内转换为代码。尽管如此,宏无法使用硬编码的名称访问该作用域中的变量,但如果标识符作为参数接收,它就可以这样做。
此外,请注意,Rust 允许使用相同名称重新声明变量。
所以,例如:
let x = 42;
print_type_of(x); //i32
let x = "foo";
print_type_of(x); //&str
完全可以工作。这两个 x
实际上不是同一个变量,它们只是具有相同的标识符("name"),后者使前者无法访问。
宏可以生成遮蔽变量的代码行。所以:
macro_rules! print_and_replace {
($var: ident) => {
println!("{}", $var);
let $var = "foo";
};
}
fn main(){
let x = 42;
print_and_replace!(x);
println!("{}", x);
}
与您编写的代码相同,如果标识符通过参数传递。如果宏尝试直接访问 x
,则不起作用。
关于为什么 tokio::pin
需要这样做,栈固定需要确保原始值不会再次移动。通过遮蔽变量,它可以确保这一点,使其不再可访问。
所以 pin
不过是这样的:
macro_rules! pin {
($var: ident) => {
let $var = unsafe{ Pin::new_unchecked(&mut $var) };
}
}
英文:
Macros are more powerful than functions. They are evaluated at compile time, and turn into code in the same scope they are invoked. Despite that, a macro can't access variables in that scope with hardcoded names, but it can if the identifier is received as a parameter.
Also note that Rust allows shadowing (redeclaring) variables with the same name.
So, for example:
let x = 42;
print_type_of(x); //i32
let x = "foo";
print_type_of(x); //&str
works just fine. The two x
'es aren't really the same variable, they just have the same identifier ("name"), and the latter makes the former inaccessible.
And a macro can emit the line that shadows the variable. So:
macro_rules! print_and_replace {
($var: ident) => {
println!("{}", $var);
let $var = "foo";
};
}
fn main(){
let x = 42;
print_and_replace!(x);
println!("{x}");
}
is the same* as if you wrote
fn main(){
let x = 42;
println!("{}", x);
let x = "foo";
println!("{x}");
}
<sup>*: if the identifier is passed in via an argument. It would not work if the macro tried to access x
directly.</sup>
As for why tokio::pin
needs to do this, stack pinning requires that the original value is never moved again. It can ensure this by shadowing the variable, making it no longer accessible.
So pin
is not much more than this:
macro_rules! pin {
($var: ident) => {
let $var = unsafe{ Pin::new_unchecked(&mut $var) };
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论