为什么生锈串似乎很难

2021-04-15 22:29:09

2493 Brandon Smith最近是我' Ve一直看到从试图进入真正挂断字符串(& str,string和他们的关系)的人的人们从事人们的轶事。超越生锈'常规挑战的所有权,可以增加一层挫折,因为弦在大多数语言中都很容易。你只是把它们加在一起,拆分它们,无论如何!它们和#39;你可以做任何你想做的原因。对于唯一知道这个心理模型的人(也就是说,从来没有用C / C ++工作),使用锈迹可以是一个粗鲁的觉醒。他们感到非常复杂,拥有所有这些限制和额外的步骤,这一切似乎都是不必要的。

它' Syexame' S甚至是从未在以前从未做过低级编程的人则为尝试的人。这是一件好事!但是当您在C / C ++中使用背景时,它会带来一些额外的挑战。

"去学习c ++并回来" ISN'这是一个非常合理的解决问题,所以我想挖掘我看到的最常见问题之一,并给出一个"悬崖笔记"有希望使RUDE的解释能够对更多人更容易获得更多的人。

字符串很简单,对吗?他们只是基因的原始词或布尔值 - 你可以用文字(" foo")来创建,通过自由复制,加上其他字符串(或基元),从函数返回, 等等。

一个字符串是一个原始的意义上,它' s根本:没有多少个程序可以在不处理字符串的情况下实现大部分价值。但它在实施方面不是一个原始的。

像数字,布尔值等其他基元的区别特征是它们具有恒定的大小。任何两个相同类型(f64,例如 - java folks的f64)占用内存中完全相同数量的字节。这意味着我们可以为变量或函数参数或其他任何可能留出空间,我们知道任何可能的F64都能适应那里。无论我们的价值多大或多少,我们都在做什么数学。

另一方面,一个字符串没有常量大小。如果你拿着字符串a和string b,他们areen' t相同(" foo"和#34; foo" s,它非常不太可能它们具有相同的大小。 " foofoo"占据emex的两倍于" foo"

从技术上讲,将有一个小数量的额外数据,如长度,所以它赢得了'恰好是大小的两倍。但它与我们的目的足够接近。

这是一个问题,因为在罩的平面数据结构下必须在编译时具有已知尺寸,即使在更高级别的语言中也是如此。任何大小可以在编译时都知道的任何数据(或随着程序运行时的主动更改)必须在堆上静置,并随着程序运行而动态分配。这在很大程度上被许多语言从您那样隐藏起来。

堆就像一个大桶内存,系统您的代码正在运行的系统可以借出。你的程序说"我需要x字节的东西,而#34;并且系统说"好的,你去这里和#34;然后,如果您的程序后来说"实际上是那个' t足够,现在我需要y字节",它' ll必须采取新块,从旧的一切复制到它中,并然后将旧的一个返回系统。这是VEC如何工作!它' s也是javascript' s阵列的工作,以及我们' ll很快找出来,它' s如何生锈' s字符串的工作。那么为什么不得不像数字一样发生这种情况?因为编译器可以继续并指定正确的蝙蝠数量的内存量。它知道它永远不会展开,因此它永远不会要求更多内存桶(堆)。

因此,当您真的达到它时,一个字符串是生活在堆中的数据结构,而不是一个原始的。粗略地说出它' s一个字符数组(在c中它实际上是一个字符数组)。当您一起添加两个字符串时,程序并在前提时知道结果是多大的,因此它需要从堆上请求内存,就像运行一样。将字符串传递给函数时,您'重新将指针指向该堆分配的数组,其中一些其他代码可以用于查找其内容。指针本身就像一个数字一样,具有常量大小。

"但字符串是不断使用的!"你可能会思考。 "我们始终切片和切割并重新混合它们;必须与它们一起工作是阵列的疯了,无论只要它们的长度变化,必须手动重新分配,复制和解除分配的阵列更少!"

by"大多数语言"我的意思是"大多数最常用的语言"这就是说javascript,python,java,c#,go,kotlin,swift等。在所有这些语言中,字符串都是不可变的。您可以将不同的字符串分配给字符串插槽,但您可能不会更改字符串本身。新人占据了它的位置,旧的一个人丢失到垃圾收集器的风中(除非一些其他代码使用它,在这种情况下它独自留下)。

垃圾收集是一种语言特征,系统可以自动弄清楚当它耗尽的某些内存是使用的,并且可以将其放回桶中,以便它可以将其丢弃给别人。

它完成了字符串只是基元的错觉:数字和布尔也是这些语言的不可变!您可以将新号码分配给A"插槽"但您无法更改数字本身。

它使得在许多方面使用字符串更简单。与数字一样,您可以将字符串传递给另一个函数(甚至是另一个线程!),而不担心它将完成的内容(而其他代码可以在不担心您可能对此的情况下接收它)。它还与自动垃圾收集很好,这些语言也有很好的套装。

Ruby实际上是一个异常值,我刚刚在研究这篇文章时学会了!尽管是一种高级别的垃圾收集语言,它确实有变形字符串。

如果你和C / C ++ / RUST的新&#39,这可能是你的弦乐模型。它&#39可能在骨骼中深入根深蒂固,并剥离了你的假设和#39; ve所形成的假设是可以理解的很难做到的事情。

好吧,好消息是他们aren' t只是字符阵列。这两种语言都会为您提供围绕该字符数组包裹的数据结构,该字符数组允许您完成合理的操作,如将一个字符串附加到另一个字符串,而无需手动跟踪其长度或根据需要重新分配底层数组。那些事情会自动发生。

坏消息(取决于您的角度来看)是语言并不像上面的语言一样隐藏它们。您必须使用/必须使用字符串作为数据结构,而不是一个原始的。在Rust中,字符串结构非常类似于字符(或Python列表,或Java ArrayList或JavaScript arrow等)的VEC,为方便起见,有一些添加的钟声和吹口哨。就像一个VEC,你可以将字符添加到最后,可以删除字符,并且可以更改中间的字符。引用相同字符串的其他代码将看到这些更新。字符串是您非常明确地创建的东西(通常通过字符串:: new()或字符串:: from()),并且可能会发生变异,然后它在脱离范围时被解除分配。

控制。 C ++和RUTR设计用于使用精细磨碎的控制有价值的情况。例如,有时您可能希望重新使用现有的字符串并替换或添加到其内容,而不是每次+使用它的其他内容分配全新的一个。给予人们这种控制级别的控制意味着呈现更复杂的心理模型。

字符串是最接近的rust for the strings'熟悉其他语言,但是当你只输入一个字符串文字时,你得到了类型& str:

A& str是一个串切片。它的参考(指针+长度)到其中一个字符阵列之一的参考(指针+长度)。一直在谈论。如果您有一个可变的切片(& mut str),那么您可以突变其内容,但是您无法长度。字符串切片始终指的是内存中的相同系列字节;您无法添加或删除字节,因为它不知道如何重新分配自己,如果需要更多的空间。它甚至知道这些字节是否在堆上。它只是一个参考。

理解Rust中弦的关键是内化每个& str需要一个住的地方。它经常生活在字符串内(我们可以通过调用.as_str()方法)从一个字符串中获取一个&当您的代码本身包含一个字符串文字,如"哈克?",字符串you'重新为您的程序本身提供指向。您的程序' s代码包含该字符数组,所以& str可以直接指向它。但它可以' t生长或缩小(实际上,在这种情况下,它可以'甚至改变 - 你可以' t得到一个& mut str到它),因为有些东西'其他东西它。

我认为第一个墙上的墙壁击中生锈串是这样的:

让答:& str ="你好" ;让B:& str ="世界" ;让c = a + b;

错误[e0369]:不能添加`& str`到`& str` - - > SRC / MAIN .RS:4:15 | 4 |让c = a + b; | - ^ - & str | | | | | `+`不能用于连接两个`& str`字符串| & str |帮助:`to_owned()`可用于从字符串引用创建所有的`string`。字符串连接将右侧的字符串附加到左侧的字符串,可能需要重新分配。这需要左侧的字符串| 4 |让c = a。 to_owned()+ b; | ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^

"什么是哎呀?我必须在我一起添加两个字符串之前调用一种方法?这种语言很糟糕!"

但在我们的新理解的背景下退回并思考它:A和B生活在我们的程序中,' s' t被修改。所以,如果我们在一起添加它们,新字符串会在哪里生活?

好吧,它'必须生活在一个字符串中。该语言并不知道提前需要大大,所以我们需要为它制作一个动态的地方,可以调整到必要的尺寸。

我们在哪里得到那个字符串?好吧,我们在那里有一些选择!我们可以单独创建一个:

让答:& str ="你好" ;让B:& str ="世界" ;让mut new_string:string = string :: new(); new_string。 push_str(a); new_string。 push_str(b);让c = new_string。 AS_STR();

那个'虽然有点笨拙。幸运的是,生锈让我们的生活更轻松。在Rust中,+运算符将在左侧和A& str右侧拍摄一个字符串,它会在左侧字符串上调用.push_str(),使其变化,使其持有&amp的副本。str&#39 ;■内容,然后字符串将是添加的结果。所以这将有效:

让c = string :: from(" hello")+"世界" ; // c是一个字符串,而不是a& str!

好的,最后一件事:什么' s那个.to_owned()方法在错误消息中建议? .to_owned()是几种数据类型实现的rust方法,并且用于恰好这种情况。来自文档:

某些类型可以通过实施克隆特征来从借来的借用。但克隆仅适用于从& t到t. Todown特性概括了克隆从给定类型的任何借用构建拥有的数据。

那个' sa bit技术,但是,当你对某些东西有一个不可变形的引用时,你需要改变它,等等(我们做的!因为我们需要把"世界"在某处),您调用此方法,它为您提供了一份您可以使用所需的副本。在& str的情况下,此副本以字符串的形式结束。所以:

当你'你有两个&你想要一起加入一个新的字符串,这可以说是最干净的方法,这就是为什么它和#39;编译器如此方便地建议的原因:

Rust是一种独特的强大语言,尽管所有的挑战,它就在某种程度上独特地访问了它。它'咒语吨的新的低级程序员否则/' ve被c / c ++吓到了。那个'太棒了!

但在它的富有成效意味着学习某些事情如何真正工作,或者你'不断与编译器(仍然比持续的虫子更好!)。希望我今天的一个科目之一,我'

总结:生锈中的每一块字符串内容都必须居住在某个地方。某处可以在一个引用的字符串文字中,在堆上的字符串内,或其他地方。当你'重新使用字符串时,您必须考虑内容实际生活/将居住的位置。如果你没有' t有一个地方来放置它,你的代码赢得了' t编译。如果参考/切片更远的字符串内容,它指向的字符串内容(借用是另一天的话题),您的代码赢得了' t编译。合并这个"哪里和#34;进入你的心理模型,你' ll有一个更容易的时间让你的代码工作。