πŸ“œ ⬆️ ⬇️

Creating a function on Rust that accepts a String or & str

From translator


The article is one of a series of posts telling about the use of some useful library types and the associated Rust idioms using the example of string data types. The information is undoubtedly useful both for beginning Rust programmers, and for those who have already managed to try themselves a little in this language, but have not quite gotten to know the rich library of types. The original post contains several inaccuracies and typos in the code, which I tried to correct during the translation process, but in general, the described approaches and motivation are correct, suitable for the concept of β€œbest practices”, and therefore deserve attention.


In my last post ( English ), we talked a lot about using &str as the preferred type for functions that take string arguments. Toward the end of the post, we discussed when it is better to use a String , and when &str in structures ( struct ). Although I think that in general the advice is good, but in some cases using &str instead of String not optimal. For such cases, we need another strategy.

Structure with String String Fields


Look at the Person structure below. For the purposes of our discussion, we assume that there is a real need in the name field. We will decide to use String instead of &str .

 struct Person { name: String, } 

Now we need to implement the new() method. Following the advice from the previous post, we prefer the &str type:

 impl Person { fn new(name: &str) -> Person { Person { name: name.to_string() } } } 

The example will work only if we do not forget about the .to_string() call in the new() method (In fact, it’s better to use the to_owned() method, because the to_string() method uses a rather heavy text formatting library to place a string in memory to_owned() simply copies the string cut &str directly to the new String object - approx . However, the usability of the function leaves much to be desired. If we use a string literal, we can create a new Person record like this: Person::new("Herman") . But if we already have the owning String String , then we need to get a link to it:
')
 let name = "Herman".to_string(); let person = Person::new(name.as_ref()); 

It seems as if we are walking in circles. First we have a String , then we call as_ref() to turn it into &str , only then to turn it back into a String inside the new() method. We could go back to using a String , like fn new(name: String) -> Person , but then we would have to force the user to constantly call .to_string() if he wants to create a Person from a string literal.

Conversions with Into


We can make our function easier to use with the help of Into . This type will automatically convert &str to String . If we already have a String , then there will be no conversion.

 struct Person { name: String } impl Person { fn new<S: Into<String>>(name: S) -> Person { Person { name: name.into() } } } fn main() { let person = Person::new("Herman"); let person = Person::new("Herman".to_string()); } 

The syntax of the new() signature is now slightly different. We use generic types ( English ) and types ( English ) to explain to Rust that some type S must implement the type Into for type String . The String type implements Into , String . &str Into .to_string() ( β€” . .) , new() . .to_string() , . , Into , β€” . Rust (.) .

, , . , , , String , &str . , fn new<S: Into>(name: S) -> Person β€” , , . , , Into . , Rust. , . , , crates.io. , , Rust .

Person::new()
where , , , , :

struct Person { name: String, } impl Person { fn new<S>(name: S) -> Person where S: Into<String> { Person { name: name.into() } } }

String &str Rust ( ) Rust, String &str ( )
Into , String . &str Into .to_string() ( β€” . .) , new() . .to_string() , . , Into , β€” . Rust (.) .

, , . , , , String , &str . , fn new<S: Into>(name: S) -> Person β€” , , . , , Into . , Rust. , . , , crates.io. , , Rust .

Person::new()
where , , , , :

struct Person { name: String, } impl Person { fn new<S>(name: S) -> Person where S: Into<String> { Person { name: name.into() } } }

String &str Rust ( ) Rust, String &str ( )

Into , String . &str Into .to_string() ( β€” . .) , new() . .to_string() , . , Into , β€” . Rust (.) .

, , . , , , String , &str . , fn new<S: Into>(name: S) -> Person β€” , , . , , Into . , Rust. , . , , crates.io. , , Rust .

Person::new()
where , , , , :

struct Person { name: String, } impl Person { fn new<S>(name: S) -> Person where S: Into<String> { Person { name: name.into() } } }

String &str Rust ( ) Rust, String &str ( )

Into , String . &str Into .to_string() ( β€” . .) , new() . .to_string() , . , Into , β€” . Rust (.) .

, , . , , , String , &str . , fn new<S: Into>(name: S) -> Person β€” , , . , , Into . , Rust. , . , , crates.io. , , Rust .

Person::new()
where , , , , :

struct Person { name: String, } impl Person { fn new<S>(name: S) -> Person where S: Into<String> { Person { name: name.into() } } }

String &str Rust ( ) Rust, String &str ( )
 Into   ,   String    .  &str  Into     .to_string() (    β€” . .) ,         new() .        .to_string() ,       .     ,        Into ,   β€” . Rust    (.)         . 

, , . , , , String , &str . , fn new<S: Into>(name: S) -> Person β€” , , . , , Into . , Rust. , . , , crates.io. , , Rust .

Person::new()
where , , , , :

struct Person { name: String, } impl Person { fn new<S>(name: S) -> Person where S: Into<String> { Person { name: name.into() } } }

String &str Rust ( ) Rust, String &str ( )
Into , String . &str Into .to_string() ( β€” . .) , new() . .to_string() , . , Into , β€” . Rust (.) .

, , . , , , String , &str . , fn new<S: Into>(name: S) -> Person β€” , , . , , Into . , Rust. , . , , crates.io. , , Rust .

Person::new()
where , , , , :

struct Person { name: String, } impl Person { fn new<S>(name: S) -> Person where S: Into<String> { Person { name: name.into() } } }

String &str Rust ( ) Rust, String &str ( )

Into , String . &str Into .to_string() ( β€” . .) , new() . .to_string() , . , Into , β€” . Rust (.) .

, , . , , , String , &str . , fn new<S: Into>(name: S) -> Person β€” , , . , , Into . , Rust. , . , , crates.io. , , Rust .

Person::new()
where , , , , :

struct Person { name: String, } impl Person { fn new<S>(name: S) -> Person where S: Into<String> { Person { name: name.into() } } }

String &str Rust ( ) Rust, String &str ( )

Into , String . &str Into .to_string() ( β€” . .) , new() . .to_string() , . , Into , β€” . Rust (.) .

, , . , , , String , &str . , fn new<S: Into>(name: S) -> Person β€” , , . , , Into . , Rust. , . , , crates.io. , , Rust .

Person::new()
where , , , , :

struct Person { name: String, } impl Person { fn new<S>(name: S) -> Person where S: Into<String> { Person { name: name.into() } } }

String &str Rust ( ) Rust, String &str ( )

Source: https://habr.com/ru/post/274455/


All Articles