kFY*_*tek 152 reference dereference formal-semantics rust
我正在学习/试验Rust,在我用这种语言找到的所有优雅中,有一个让我感到困惑并且看起来完全不合适的特点.
在进行方法调用时,Rust会自动取消引用指针.我做了一些测试来确定确切的行为:
struct X { val: i32 }
impl std::ops::Deref for X {
type Target = i32;
fn deref(&self) -> &i32 { &self.val }
}
trait M { fn m(self); }
impl M for i32 { fn m(self) { println!("i32::m()"); } }
impl M for X { fn m(self) { println!("X::m()"); } }
impl M for &X { fn m(self) { println!("&X::m()"); } }
impl M for &&X { fn m(self) { println!("&&X::m()"); } }
impl M for &&&X { fn m(self) { println!("&&&X::m()"); } }
trait RefM { fn refm(&self); }
impl RefM for i32 { fn refm(&self) { println!("i32::refm()"); } }
impl RefM for X { fn refm(&self) { println!("X::refm()"); } }
impl RefM for &X { fn refm(&self) { println!("&X::refm()"); } }
impl RefM for &&X { fn refm(&self) { println!("&&X::refm()"); } }
impl RefM for &&&X { fn refm(&self) { println!("&&&X::refm()"); } }
struct Y { val: i32 }
impl std::ops::Deref for Y {
type Target = i32;
fn deref(&self) -> &i32 { &self.val }
}
struct Z { val: Y }
impl std::ops::Deref for Z {
type Target = Y;
fn deref(&self) -> &Y { &self.val }
}
#[derive(Clone, Copy)]
struct A;
impl M for A { fn m(self) { println!("A::m()"); } }
impl M for &&&A { fn m(self) { println!("&&&A::m()"); } }
impl RefM for A { fn refm(&self) { println!("A::refm()"); } }
impl RefM for &&&A { fn refm(&self) { println!("&&&A::refm()"); } }
fn main() {
// I'll use @ to denote left side of the dot operator
(*X{val:42}).m(); // i32::m() , Self == @
X{val:42}.m(); // X::m() , Self == @
(&X{val:42}).m(); // &X::m() , Self == @
(&&X{val:42}).m(); // &&X::m() , Self == @
(&&&X{val:42}).m(); // &&&X:m() , Self == @
(&&&&X{val:42}).m(); // &&&X::m() , Self == *@
(&&&&&X{val:42}).m(); // &&&X::m() , Self == **@
println!("-------------------------");
(*X{val:42}).refm(); // i32::refm() , Self == @
X{val:42}.refm(); // X::refm() , Self == @
(&X{val:42}).refm(); // X::refm() , Self == *@
(&&X{val:42}).refm(); // &X::refm() , Self == *@
(&&&X{val:42}).refm(); // &&X::refm() , Self == *@
(&&&&X{val:42}).refm(); // &&&X::refm(), Self == *@
(&&&&&X{val:42}).refm(); // &&&X::refm(), Self == **@
println!("-------------------------");
Y{val:42}.refm(); // i32::refm() , Self == *@
Z{val:Y{val:42}}.refm(); // i32::refm() , Self == **@
println!("-------------------------");
A.m(); // A::m() , Self == @
// without the Copy trait, (&A).m() would be a compilation error:
// cannot move out of borrowed content
(&A).m(); // A::m() , Self == *@
(&&A).m(); // &&&A::m() , Self == &@
(&&&A).m(); // &&&A::m() , Self == @
A.refm(); // A::refm() , Self == @
(&A).refm(); // A::refm() , Self == *@
(&&A).refm(); // A::refm() , Self == **@
(&&&A).refm(); // &&&A::refm(), Self == @
}
Run Code Online (Sandbox Code Playgroud)
所以,似乎或多或少:
&self
(call-by-reference)声明的方法时:
self
self
self
对类型使用(call-by-value)T
声明的方法就像使用&self
(call-by-reference)类型声明它们一样,&T
并且对点运算符左侧的引用调用.Deref
则使用带有trait 的重载.什么是确切的自动解除引用规则?任何人都可以为这样的设计决定提供任何正式的理由吗?
huo*_*uon 121
你的伪代码非常正确.对于这个例子,假设我们有一个方法调用foo.bar()
where foo: T
.我将使用完全限定语法(FQS)来明确调用方法的类型,例如A::bar(foo)
或A::bar(&***foo)
.我只是要写一堆随机大写字母,每一个都只是一些任意类型/特征,除了T
始终foo
是调用该方法的原始变量的类型.
算法的核心是:
U
(即设置U = T
然后U = *T
,......)
bar
接收器类型(方法中的类型self
)U
完全匹配的方法,请使用它("按值方法")&
或&mut
of receiver),如果某个方法的接收器匹配&U
,则使用它("autorefd方法")值得注意的是,一切都考虑了方法的"接收器类型",而不是特征的Self
类型,即impl ... for Foo { fn method(&self) {} }
考虑&Foo
何时匹配方法,并fn method2(&mut self)
考虑&mut Foo
匹配时.
如果在内部步骤中有多个特征方法有效,那么这是一个错误(也就是说,在每个1或2中只能有零个或一个特征方法有效,但每个方法有效一个:一个从1开始将被采用,并且固有方法优先于特质方法.如果我们到达循环的末尾而没有找到匹配的东西,那也是一个错误.具有递归Deref
实现也是错误的,这使得循环无限(它们将达到"递归限制").
这些规则似乎在大多数情况下都是这样做的,尽管能够编写明确的FQS表单在某些边缘情况下非常有用,并且对于宏生成的代码也是非常有用的.
只添加一个自动引用,因为
&foo
保留了一个强大的连接foo
(它是foo
自己的地址),但是更多的开始失去它:&&foo
是存储堆栈上的一些临时变量的地址&foo
.假设我们有一个电话foo.refm()
,如果foo
有类型:
X
,然后我们开始U = X
,refm
有接收器类型&...
,所以步骤1不匹配,采取自动引用给我们&X
,这确实匹配(与Self = X
),所以呼叫是RefM::refm(&foo)
&X
,开始于U = &X
,&self
在第一步(与Self = X
)匹配 ,所以调用是RefM::refm(foo)
&&&&&X
,这与任一步都不匹配(特征没有实现&&&&X
或者&&&&&X
),所以我们取消引用一次得到U = &&&&X
,匹配1(带Self = &&&X
)并且调用是RefM::refm(*foo)
Z
,不匹配任何一步,所以它被解除引用一次,得到Y
,也不匹配,所以它再次被取消引用,得到X
,它不匹配1,但在自动反射后匹配,所以调用是RefM::refm(&**foo)
.&&A
,1.不匹配,也不匹配2.因为特征没有实现&A
(for 1)或&&A
(for 2),所以它被解除引用&A
,匹配1.,Self = A
假设我们有foo.m()
,而A
不是Copy
,如果foo
有类型:
A
,然后U = A
匹配self
直接使呼叫M::m(foo)
与Self = A
&A
,然后1.不匹配,也不匹配2.(既没有&A
也没有&&A
实现特征),所以它被解除引用A
,它匹配,但M::m(*foo)
需要A
按值取走因此移出foo
,因此错误.&&A
1.不匹配,但autorefing给人&&&A
,这确实匹配,所以调用的是M::m(&foo)
与Self = &&&A
.(这个答案基于代码,并且与(稍微过时的)README相当接近.编译器/语言的这一部分的主要作者Niko Matsakis也对这个答案进行了瞥.)
Luk*_*odt 18
Rust 参考有一章关于方法调用表达式。我复制了下面最重要的部分。提醒:我们说的是一个表达式recv.m()
,recv
下面称为“接收器表达式”。
第一步是构建候选接收器类型列表。通过反复取消引用接收者表达式的类型,将遇到的每个类型添加到列表中,然后最后尝试进行无大小强制转换,如果成功,则添加结果类型来获得这些。然后,对于每个候选者
T
,在 之后立即将&T
和添加&mut T
到列表中T
。例如,如果接收者有 type
Box<[i32;2]>
,那么候选类型将是Box<[i32;2]>
,&Box<[i32;2]>
,&mut Box<[i32;2]>
,[i32; 2]
(通过解引用),&[i32; 2]
,&mut [i32; 2]
,[i32]
(通过无大小强制转换),&[i32]
, 和 finally&mut [i32]
。然后,对于每个候选类型
T
,在以下位置搜索具有该类型接收器的可见方法:
T
的固有方法(直接在T
[¹]上实现的方法)。- 由 实现的可见特征提供的任何方法
T
。[...]
(关于 [¹] 的注意事项:我实际上认为这个措辞是错误的。我已经打开了一个问题。让我们忽略括号中的那句话。)
让我们从您的代码中详细查看一些示例!对于您的示例,我们可以忽略有关“无大小强制”和“固有方法”的部分。
(*X{val:42}).m()
:接收者表达式的类型是i32
。我们执行以下步骤:
i32
不能取消引用,所以我们已经完成了第 1 步。列出: [i32]
&i32
和&mut i32
。列表:[i32, &i32, &mut i32]
<i32 as M>::m
接收器类型为i32
。所以我们已经完成了。到目前为止很容易。现在让我们选择一个更难的例子:(&&A).m()
. 接收者表达式的类型是&&A
。我们执行以下步骤:
&&A
可以取消对 的引用&A
,因此我们将其添加到列表中。&A
可以再次取消引用,所以我们也添加A
到列表中。A
不能取消引用,所以我们停止。列表:[&&A, &A, A]
T
列表中,我们添加&T
和&mut T
后T
。列表:[&&A, &&&A, &mut &&A, &A, &&A, &mut &A, A, &A, &mut A]
&&A
,所以我们转到列表中的下一个类型。<&&&A as M>::m
确实具有接收器类型的方法&&&A
。所以我们完成了。以下是所有示例的候选接收器列表。包含在?x?
其中的类型是“获胜”的类型,即可以找到拟合方法的第一种类型。还要记住,列表中的第一个类型始终是接收者表达式的类型。最后,我将列表格式化为三行,但这只是格式化:这个列表是一个平面列表。
(*X{val:42}).m()
? <i32 as M>::m
[?i32?, &i32, &mut i32]
Run Code Online (Sandbox Code Playgroud)X{val:42}.m()
? <X as M>::m
[?X?, &X, &mut X,
i32, &i32, &mut i32]
Run Code Online (Sandbox Code Playgroud)(&X{val:42}).m()
? <&X as M>::m
[?&X?, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
Run Code Online (Sandbox Code Playgroud)(&&X{val:42}).m()
? <&&X as M>::m
[?&&X?, &&&X, &mut &&X,
&X, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
Run Code Online (Sandbox Code Playgroud)(&&&X{val:42}).m()
? <&&&X as M>::m
[?&&&X?, &&&&X, &mut &&&X,
&&X, &&&X, &mut &&X,
&X, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
Run Code Online (Sandbox Code Playgroud)(&&&&X{val:42}).m()
? <&&&X as M>::m
[&&&&X, &&&&&X, &mut &&&&X,
?&&&X?, &&&&X, &mut &&&X,
&&X, &&&X, &mut &&X,
&X, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
Run Code Online (Sandbox Code Playgroud)(&&&&&X{val:42}).m()
? <&&&X as M>::m
[&&&&&X, &&&&&&X, &mut &&&&&X,
&&&&X, &&&&&X, &mut &&&&X,
?&&&X?, &&&&X, &mut &&&X,
&&X, &&&X, &mut &&X,
&X, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
Run Code Online (Sandbox Code Playgroud)(*X{val:42}).refm()
? <i32 as RefM>::refm
[i32, ?&i32?, &mut i32]
Run Code Online (Sandbox Code Playgroud)X{val:42}.refm()
? <X as RefM>::refm
[X, ?&X?, &mut X,
i32, &i32, &mut i32]
Run Code Online (Sandbox Code Playgroud)(&X{val:42}).refm()
? <X as RefM>::refm
[?&X?, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
Run Code Online (Sandbox Code Playgroud)(&&X{val:42}).refm()
? <&X as RefM>::refm
[?&&X?, &&&X, &mut &&X,
&X, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
Run Code Online (Sandbox Code Playgroud)(&&&X{val:42}).refm()
? <&&X as RefM>::refm
[?&&&X?, &&&&X, &mut &&&X,
&&X, &&&X, &mut &&X,
&X, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
Run Code Online (Sandbox Code Playgroud)(&&&&X{val:42}).refm()
? <&&&X as RefM>::refm
[?&&&&X?, &&&&&X, &mut &&&&X,
&&&X, &&&&X, &mut &&&X,
&&X, &&&X, &mut &&X,
&X, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
Run Code Online (Sandbox Code Playgroud)(&&&&&X{val:42}).refm()
? <&&&X as RefM>::refm
[&&&&&X, &&&&&&X, &mut &&&&&X,
?&&&&X?, &&&&&X, &mut &&&&X,
&&&X, &&&&X, &mut &&&X,
&&X, &&&X, &mut &&X,
&X, &&X, &mut &X,
X, &X, &mut X,
i32, &i32, &mut i32]
Run Code Online (Sandbox Code Playgroud)Y{val:42}.refm()
? <i32 as RefM>::refm
[Y, &Y, &mut Y,
i32, ?&i32?, &mut i32]
Run Code Online (Sandbox Code Playgroud)Z{val:Y{val:42}}.refm()
? <i32 as RefM>::refm
[Z, &Z, &mut Z,
Y, &Y, &mut Y,
i32, ?&i32?, &mut i32]
Run Code Online (Sandbox Code Playgroud)A.m()
? <A as M>::m
[?A?, &A, &mut A]
Run Code Online (Sandbox Code Playgroud)(&A).m()
? <A as M>::m
[&A, &&A, &mut &A,
?A?, &A, &mut A]
Run Code Online (Sandbox Code Playgroud)(&&A).m()
? <&&&A as M>::m
[&&A, ?&&&A?, &mut &&A,
&A, &&A, &mut &A,
A, &A, &mut A]
Run Code Online (Sandbox Code Playgroud)(&&&A).m()
? <&&&A as M>::m
[?&&&A?, &&&&A, &mut &&&A,
&&A, &&&A, &mut &&A,
&A, &&A, &mut &A,
A, &A, &mut A]
Run Code Online (Sandbox Code Playgroud)A.refm()
? <A as RefM>::refm
[A, ?&A?, &mut A]
Run Code Online (Sandbox Code Playgroud)(&A).refm()
? <A as RefM>::refm
[?&A?, &&A, &mut &A,
A, &A, &mut A]
Run Code Online (Sandbox Code Playgroud)(&&A).refm()
? <A as RefM>::refm
[&&A, &&&A, &mut &&A,
?&A?, &&A, &mut &A,
A, &A, &mut A]
Run Code Online (Sandbox Code Playgroud)(&&&A).refm()
? <&&&A as RefM>::refm
[&&&A, ?&&&&A?, &mut &&&A,
&&A, &&&A, &mut &&A,
&A, &&A, &mut &A,
A, &A, &mut A]
Run Code Online (Sandbox Code Playgroud) 归档时间: |
|
查看次数: |
18096 次 |
最近记录: |