to_string()
to make the program compile. And I hope to shed some light on the question of why Rust has two string types, String
and &str
.String
. Our search will lead us to the type std::string::String
, which is not bad at first. fn print_me(msg: String) { println!(": {}", msg); } fn main() { let message = ", "; print_me(message); }
expected `collections::string::String`, found `&'static str`
&str
not compatible with the type String
. We need to change the type of the message
variable to String
so that the compilation succeeds: let message = ", ".to_string();
. It will work this way, but it's like using clone()
to fix ownership-inheritance errors. Here are three reasons to change the type of the print_me
argument to &str
:&
denotes the reference type, that is, we give the variable a loan . When print_me
finishes working with a variable, ownership is returned to its original owner. If we don't have a good reason to transfer ownership of the message
variable to our function, we should use borrowing.String
for message
means the program should copy the value. When using a link such as &str
, copying does not occur.String
can magically turn into &str
using the Deref
type and type casting. An example will allow to understand this moment much better.print_me
function. The main point by which all this works is the transfer of values by reference. Instead of passing the owned string owned_string
as a String
, we pass it as a &String
pointer. When the compiler sees that &String
passed to a function that accepts &str
, it leads &String
to &str
. Exactly the same conversion is used when using strings with a normal and atomic reference counter. The variable string
already a reference, so there is no need to use &
when calling print_me(string)
. With this knowledge, we no longer need to constantly call .to_string()
on our code. fn print_me(msg: &str) { println!("msg = {}", msg); } fn main() { let string = ", "; print_me(string); let owned_string = ", ".to_string(); // String::from_str(", ") print_me(&owned_string); let counted_string = std::rc::Rc::new(", ".to_string()); print_me(&counted_string); let atomically_counted_string = std::sync::Arc::new(", ".to_string()); print_me(&atomically_counted_string); }
Vec
. All the same, String
is just a vector of eight-byte characters. You can read more about coercion with dereferencing ( English ) in the book “ Rust Programming Language ” ( English ).to_string()
. However, we may have some problems when using structures. Using existing knowledge, we could create such a structure: struct Person { name: &str, } fn main() { let _person = Person { name: "Herman" }; }
<anon>:2:11: 2:15 error: missing lifetime specifier [E0106] <anon>:2 name: &str,
Person
cannot survive the name
reference. If Person
survives the name
, there is a risk of the program crashing. The main goal of Rust is to prevent this. Let's make this code compile. We need to specify the time of life ( English ), or scope, so that Rust could provide us security. Typically, the lifetime is called: 'a
. I do not know where this tradition came from, but we will follow it. struct Person { name: &'a str,' } fn main() { let _person = Person { name: "Herman" }; }
<anon>:2:12: 2:14 error: use of undeclared lifetime name `'a` [E0261] <anon>:2 name: &'a str,
Person
structure should not survive the name
field. So we need to declare the lifetime of the Person
structure. Short searches lead us to syntax for declaring the lifetime: <'a>
. struct Person<'a> { name: &'a str, } fn main() { let _person = Person { name: "Herman" }; }
greet
method to our Person
class. struct Person<'a> { name: &'a str, } impl Person { fn greet(&self) { println!(", {}", self.name); } } fn main() { let person = Person { name: "Herman" }; person.greet(); }
<anon>:5:6: 5:12 error: wrong number of lifetime parameters: expected 1, found 0 [E0107] <anon>:5 impl Person {
Person
structure has a lifetime parameter, so our implementation should also have it. Let's declare the lifetime 'a
in the implementation of Person
like this: impl Person<'a> {
. Alas, now we get this strange compilation error: <anon>:5:13: 5:15 error: use of undeclared lifetime name `'a` [E0261] <anon>:5 impl Person<'a> {
impl
like this: impl<'a> Person {
. We compile again, we get an error: <anon>:5:10: 5:16 error: wrong number of lifetime parameters: expected 1, found 0 [E0107] <anon>:5 impl<'a> Person {
Person
structure to its implementation like this: impl<'a> Person<'a> {
. Now the program will be compiled. Here is the full working code: struct Person<'a> { name: &'a str, } impl<'a> Person<'a> { fn greet(&self) { println!(", {}", self.name); } } fn main() { let person = Person { name: "Herman" }; person.greet(); }
String
be used, and when &str
in structures? In other words, when should I use a reference to another type in a structure? We should use references if our structure does not require ownership of the variable. The meaning may be slightly blurred, so I use several rules to answer this question. struct Person { name: String, } impl Person { fn greet(&self) { println!(", {}", self.name); } } fn main() { let name = String::from_str("Herman"); let person = Person { name: name }; person.greet(); println!(" {}", name); // move error }
Encoder
structure does not need to own the variable writer
, which implements the type std::fmt::Write
, so only borrowing is used. Actually String
implements Write
. In this example, when using the encode
function, a variable of type String
passed to the Encoder
and then returned back to encode
.String
buffer with a large amount of data. Copying it every time you transfer to another function can slow down the program significantly.&str
, String
or even with a reference count. We can also create structures that contain links. The lifetime of the structure is related to the references contained in it, so that the structure cannot survive the variables to which it refers, and thus lead to errors in the program. And now we have a basic understanding of when to use links within structures, and when not.'static
lifetime (as in the first example) to make our example compile, but I wouldn’t advise you to do this: struct Person { name: &'static str, } impl Person { fn greet(&self) { println!(", {}", self.name); } } fn main() { let person = Person { name: "Herman" }; person.greet(); }
'static
valid throughout the life of the program. You hardly want Person
or name
live for so long. (For example, static string literals compiled into the program itself have the type &'static str
, that is, they live throughout the life of the program - approx. Transl.)Source: https://habr.com/ru/post/274485/
All Articles