掌握生锈的所有权

2021-04-10 22:38:40

由于堆栈溢出进行的开发人员调查中,rust连续第五年出现了第五年。开发商喜欢RUDE的原因有各种原因,其中一个是它的记忆安全保障。

Rust保证内存安全,具有称为所有权的功能。所有权与其他语言中的垃圾收集器不同,因为它只是由一组规则组成,编译器需要在运行时检查。

在某些需要垃圾收集器的语言中,开发人员需要明确地分配工作和免费内存空间。当它涉及大量内存分配时,这可以很快变得繁琐和挑战。

值得庆幸的是,处理内存分配是Rust中的所有权功能的目的。要了解所有权如何工作,让我们从更深入地了解堆栈和堆。

堆栈和堆是内存存储功能,可用于在运行时使用的代码。对于大多数编程语言,开发人员通常不会担心内存分配如何在堆栈和堆上。但是,因为rust是一个系统编程语言,如何存储值(在堆栈或堆中)对于语言的行为至关重要。

这是记忆如何存储在堆栈中的一个例子:让我们想到表上的一堆书。这些书籍以最后一本书放在堆栈顶部的方式,第一本书位于底部。理想情况下,我们不想从堆栈下面滑出底部书,在读取读取的顶部会更容易。

这正是内存如何存储在堆栈中;它使用最后一个,先进的方法。在这里,它将值存储在其获取它们的顺序中,但以相反的顺序删除它们。值得注意的是,存储在堆栈中的所有数据都具有已知尺寸。

堆中的内存分配与堆栈中的内存分配不同。想想为朋友买一件衬衫。你不知道你的朋友穿着的精确尺寸衬衫,但经常看到他,你认为他可能是中等或大。虽然你并不完全确定,你买了很大的东西,因为即使他是一个媒介,他仍然能够身体健康。这就是堆中的内存分配方式。当您有一个值(您的朋友)时,您不知道它需要的确切内存量(T恤的大小),您可以请求值的特定空间。分配器在堆中找到一个足够大的堆,并将该点标记在使用中。这是堆栈和堆之间的一个重要区别:我们不需要知道存储在堆中的值的确切大小。

与堆栈相比,堆中没有组织。很容易将数据推入堆栈,因为一切都是组织并遵循一个过程。系统理解,当您将值推入它留在顶部时,当您需要从堆栈中取出值时,您正在检索存储的最后一个值。

但是,这是堆中的情况。分配在堆上涉及搜索足够大的空白区域以匹配您请求的内存量,并将地址返回到将存储在堆栈中的位置。从堆中检索值要求您遵循指向该值存储在堆中的位置的指针。

分配在堆上看起来像书籍索引,其中存储在堆中的值的指针存储在堆栈中。但是,分配器还需要搜索足够大以包含该值的空白空间。

所有权功能管理堆栈中的内存分配以及堆,因此您不必通过此复杂的分配过程进行。尽管如此,如果您不理解堆栈和堆中的内存分配基础以及所有权能够负责这些,您的程序将以意想不到的方式行事。

所有权有三个基本规则,预测内存如何存储在堆栈中以及堆中:

当所有者超出范围时,该值将被删除:fn main(){{// scope beginslet s = string :: from(" hello"); // s进入范围} //此时s的值掉落,它超出了范围}

在我们的介绍中,我们建立了一个事实,所有权不像垃圾收集器系统,事实上,RUDE不处理垃圾收集器系统。大多数编程语言都使用垃圾收集器,或者需要开发人员分配和释放内存本身。

在所有权中,我们为自己申请记忆,当所有者超出范围时,值将被删除,内存释放。这正是第三个所有权规则解释的内容。为了更好地了解这是如何工作的,让我们来看看一个例子:

< // stackfn main()中的内存分配{{// a不是有效的赫链A = 5; // a有效//与a} // println做的东西!(" {}" a)a不再有效,它不在范围}

这个例子非常简单;这就是堆栈中的内存分配方式。由于我们知道其值5将占用的确切空间,因此将斑点分配给A.但是,这并不总是如此。有时,您需要为您的编译时不知道它的大小来分配内存空间。

对于这种情况,内存在堆上分配,首先必须请求内存,如下例所示:

fn main(){{让mut s = string :: from(" hello"); // s是有效的,从这一点开始s.push_str(" world!"); // push_str()将文字附加到字符串println!(" {}}",s); //这将打印“你好,世界!`} // s在这里不再有效}

我们可以随着我们想要的,因为它是可变的,因此可以将其追加,因为它是可变的,使得难以知道在编译时所需的精确尺寸。因此,我们将需要内存空间我们程序中字符串的大小:

让mut s = string :: from(" hello")//请求堆中的空格,字符串的大小。 当变量超出范围时,Rust所有权功能允许返回内存(释放)。 在本节中,我们将研究所有权如何影响生锈中的某些功能,从克隆和复制功能开始。 对于具有像整数的已知大小的值,将值复制到另一个值更容易: fn main(){让a =" 5&#34 ;; 让B = a; //将值a复制到b println!(" {}" a)// 5 println!(" {}" b)// 5} 因为a存储在堆栈中,所以更容易复制其值以使B为B的另一个副本。 这不是存储在堆中的值的情况: fn main(){让a = string :: from(" hello"); 让B = a; //将值A复制到B println!(" {}}}}})//这将抛出错误,因为A已经忘记了Println!(" {}}", b)// hello}

运行命令时,您将收到错误错误[E0382]:借用移动的值:" A"早些时候,我解释了堆中的值如何存储如索引过程,其中指针存储在堆栈中。

复制存储在堆上的值时,系统只会自动复制指针,留出堆数据。

这使RUDER渲染A不再有效,因此不会发生双自由错误。当您尝试释放已经释放的内存时会发生错误。由于A和B使用指针访问堆中的一个内存,因此当A熄灭范围并清除内存时,B会想要清除相同的内存,因此双重无空闲错误。

要访问A和B,您必须使用称为Clone方法的功能:

fn main(){让a = string :: from(" hello");让B = A.Clone(); println!(" a = {},b = {}" a,b); // a = hello,b = hello}

将值分配给函数遵循相同的所有权规则,这意味着它们一次只能拥有一个所有者,并释放内存一旦超出范围。让我们在Rust的文档中查看这个例子:

fn main(){let s1 = subsownship(); // sugdowshiper将其返回//值移动到s1 let s2 = string :: from(" hello"); // S2进入范围,Let S3 = Pretinggivesback(S2); // S2被移动到// readlandgivesback,它也是//将其返回值移动到S3} //此处,S3超出范围并丢弃。 S2超出范围,但被移动,所以没有发生任何事情。 S1脱离了范围,被删除了.Fn subsownship() - > string {// sugdowshipers将其//返回值移动到调用它的函数//它让somestring = string :: from(" hello"); // somestring进入范围somestring // somestring被返回,//向调用//函数} //} // retandgivesback将采用字符串并返回onyfn retandgivesback(artring:string) - >字符串{// restring进入//范围restring //返回Astring并向呼叫功能移出}

第二个所有权规则(每个值一次只能拥有一个所有者)使编写功能过于详细,只要您希望在上面示例中所见的情况下返回函数的所有权。

要返回多个值,Rust开发人员使用元组,但这往往需要花费大量时间。最好的方法是使用生锈引用功能。

使用引用,您可以使用具有对象作为参数的对象的函数而不是占用该值的所有权。与Ampersands(&)可以在不采取所有权的情况下参考价值。我们的示例功能现在可以通过这种方式写入:

fn main(){let s1 =& saveDhipership(); //将其返回值移动到S1 Let S2 = String :: from(" Hello"); // S2进入范围,Let S3 = Pretinggivesback(S2); // S2被移入S3 Println!(" {}}",s1); println!(" {}}",s3); //返回值进入S3} //这里,S3脱离范围并删除。 S2超出范围,但被移动,所以没有发生任何事情。 S1脱离了范围,被删除了.Fn subsownship() - > string {// sugdowshipers将其//返回值移动到调用它的函数//它让somestring = string :: from(" hello"); // somestring进入范围somestring // somestring被返回,//向调用//函数} //} // retandgivesback将采用字符串并返回onyfn retandgivesback(artring:string) - >字符串{// restring进入//范围restring //返回Astring并向呼叫功能移出}

如何使用引用的另一个好示例是rust文档中显示的此示例:

fn main(){let s1 = string :: from(" hello");让len = compulate_length(& s1); println!("' {}' {}。",s1,len); fn compulate_length(s:& string) - > Usize {S.Len()}

您可以在序列中引用彼此相邻的元素,而不是引用整个集合。为此,您可以使用Frose中的切片类型。但是,此功能没有与参考和借用等所有权。

让我们来看看下面的例子。在此示例中,我们将使用切片类型以连续序列的值的引用元素:

fn main(){let s = string :: from("尼日利亚");让a =& s [0..4]; //' t转移所有权,但引用前四个字母。让B =& s [4..8]; //' t转移所有权,但引用了最后四个字母。 Println!(" {}}}" a); //打印nige println!(" {}" b); //打印里亚}

所有权是生锈的重要特色。 Rust Developer越多了解所有权,他或她越容易写出可扩展代码。许多开发人员爱Rust的原因是因为这个功能,一旦你掌握它,你可以编写有效的代码并预测结果,而没有生锈的速度将速度释放出来!

在本文中,我们已经看到了所有权的基础,其规则以及如何在我们的计划中应用它们。我们还看了一些Rust的功能,没有所有权以及如何完美地使用它们。要获取RUST的所有权功能的更多提示,请查看他们的文档。

调试生锈应用可能是困难的,特别是当用户遇到难以重现的问题时。如果您有兴趣监控和跟踪生锈应用的性能,自动浮出误差,以及跟踪慢速网络请求和负载时间,请尝试Logrocket。 Logrocket就像Web应用程序的DVR,字面上录制了生锈应用程序上发生的一切。而不是猜测为什么问题发生,而是可以聚合和报告您的应用程序在发生问题时的状态。 Logrocket还监视您的应用程序的性能,报告客户端CPU负载,客户端内存用法等度量标准。