scope_and_lifetime

Scope vs Lifetime

前因

最近突发奇想看了一下rust的教程发现Scope和lifetime两个词经常出现,在看rust之前我忽视了lifetime或者说我把二者等价为一个意思,其实在写C++的时候也已经有意识的注意到悬挂指针和悬挂引用的存在了,不过从来没有用lifetime来解释。以下的例子全部用c++来举,因为我并不会rust,但是会涉及到rust的一些概念。

定义

二者正式的定义如下

Scope is the region or section of code where a variable can be accessed.

Lifetime is the time duration where an object/variable is in a valid state.

Scope

翻译一下就是Scope是指变量可以被访问的代码区间,这是一种静态的意思,也就是说我们只要看code就能看出哪些变量是可以访问的。

1
2
3
4
5
6
7
// example 1
{ // a
{ // b
int a;
}
a = 1;
}

可以看到a的scope是在注释b所在的大括号和小括号之间,这指定了a变量可以被访问的区间。所以a=1是不合法的,因为这个访问不在scope之内。

Lifetime

lifetime的定义翻译一下就是指变量处于合法状态的时间区间,这里要注意的是合法状态的意思,在没有引用或者指针之前,这是一个静态的概念,因为变量只要能被访问就是合法的,也就是说lifetime就是scope的区间。但是有了指针和引用的概念后,这其实是一个动态的概念,因为指针和引用的合法性不仅取决于它们的scope,更取决于它们指向的变量的scope,之所以说是动态的概念是因为指向性有时是只有运行时才能确定的。

1
2
3
4
5
6
7
8
9
// example 2
int a;
int b;
int *p;
if ( some_condition ) {
p = &a;
} else {
p = &b;
}

PS: 也就是说对于非引用/指针的变量来说scope其实和lifetime是相同的概念,但是对于引用/指针变量,其lifetime不等于其scope,而等于其指向变量的lifetime,如果一个引用/指针变量的lifetime小于其scope,意味着在它可以被访问的区间内,它不一定是合法的,也就是悬挂引用/指针。

1
2
3
4
5
6
7
// example 3
int* p;
{
int a;
p = &a;
}
f(*p);

another example

1
2
3
4
5
6
7
8
9
// example 4
int* f1(){
int a;
return &a;
}

int main() {
int *p = f1();
}

上面两个例子中指针p的lifetime都等于变量a的lifetime,小于p的scope,这意味着p是不合法的。

一般rust可以检测出像上面两个例子的不合法性,但是下面这个例子就不行了

1
2
3
4
5
6
7
8
9
10
11
// example 5
int* f(int* a, int* b) {
if (some_condition) {
return a;
} else {
return b;
}
}

int a, b;
int *p = f(&a, &b);

指针p的lifetime是动态确定的,编译器无法确认p的lifetime,也就无法确认p是否是合法的指针。

处理合法性

之前也提到对于简单的情况下(example 3,4),rust可以直接判断引用的lifetime,对于复杂的情形(example 5),rust有引入人工标记的方式指明引用的lifetime的限制用于判断合法性。

至于c++呢,c++直接裸奔管都不管你是不是合法的,直接UB。

参考

[1] https://stackoverflow.com/questions/11137516/scope-vs-lifetime-of-variable
[2] https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html
[3] https://zhuanlan.zhihu.com/p/27571264?group_id=862978524611497984