Sebagai pendatang baru di Rust, pemahaman saya adalah bahwa kehidupan eksplisit melayani dua tujuan.
Menempatkan anotasi seumur hidup eksplisit pada fungsi membatasi jenis kode yang mungkin muncul di dalam fungsi itu. Masa hidup yang eksplisit memungkinkan kompiler memastikan bahwa program Anda melakukan apa yang Anda inginkan.
Jika Anda (kompiler) ingin memeriksa apakah sepotong kode valid, Anda (kompiler) tidak akan harus melihat ke dalam setiap fungsi yang dipanggil secara iteratif. Cukuplah untuk melihat penjelasan fungsi yang secara langsung dipanggil oleh potongan kode tersebut. Ini membuat program Anda lebih mudah untuk dipikirkan untuk Anda (kompiler), dan membuat waktu kompilasi dapat dikelola.
Pada poin 1., Pertimbangkan program berikut yang ditulis dalam Python:
import pandas as pd
import numpy as np
def second_row(ar):
return ar[0]
def work(second):
df = pd.DataFrame(data=second)
df.loc[0, 0] = 1
def main():
# .. load data ..
ar = np.array([[0, 0], [0, 0]])
# .. do some work on second row ..
second = second_row(ar)
work(second)
# .. much later ..
print(repr(ar))
if __name__=="__main__":
main()
yang akan dicetak
array([[1, 0],
[0, 0]])
Perilaku seperti ini selalu mengejutkan saya. Apa yang terjadi adalah df
berbagi memori ar
, jadi ketika beberapa konten df
berubahwork
, perubahan itu juga menginfeksi ar
. Namun, dalam beberapa kasus ini mungkin persis seperti yang Anda inginkan, karena alasan efisiensi memori (tanpa salinan). Masalah sebenarnya dalam kode ini adalah fungsinyasecond_row
mengembalikan baris pertama, bukan yang kedua; semoga berhasil debugging itu.
Pertimbangkan program serupa yang ditulis dalam Rust:
#[derive(Debug)]
struct Array<'a, 'b>(&'a mut [i32], &'b mut [i32]);
impl<'a, 'b> Array<'a, 'b> {
fn second_row(&mut self) -> &mut &'b mut [i32] {
&mut self.0
}
}
fn work(second: &mut [i32]) {
second[0] = 1;
}
fn main() {
// .. load data ..
let ar1 = &mut [0, 0][..];
let ar2 = &mut [0, 0][..];
let mut ar = Array(ar1, ar2);
// .. do some work on second row ..
{
let second = ar.second_row();
work(second);
}
// .. much later ..
println!("{:?}", ar);
}
Kompilasi ini, Anda dapatkan
error[E0308]: mismatched types
--> src/main.rs:6:13
|
6 | &mut self.0
| ^^^^^^^^^^^ lifetime mismatch
|
= note: expected type `&mut &'b mut [i32]`
found type `&mut &'a mut [i32]`
note: the lifetime 'b as defined on the impl at 4:5...
--> src/main.rs:4:5
|
4 | impl<'a, 'b> Array<'a, 'b> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...does not necessarily outlive the lifetime 'a as defined on the impl at 4:5
--> src/main.rs:4:5
|
4 | impl<'a, 'b> Array<'a, 'b> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
Bahkan Anda mendapatkan dua kesalahan, ada juga satu dengan peran 'a
dan 'b
dipertukarkan. Melihat anotasi second_row
, kami menemukan bahwa output harus &mut &'b mut [i32]
, yaitu, output seharusnya menjadi referensi ke referensi dengan masa pakai 'b
(masa baris kedua Array
). Namun, karena kita mengembalikan baris pertama (yang memiliki masa pakai 'a
), kompiler mengeluh tentang ketidakcocokan seumur hidup. Di tempat yang tepat. Di waktu yang tepat. Debugging sangat mudah.