Developers Club geek daily blog

1 year, 8 months ago

From the translator


КДПВArticle — one of a series of the posts telling about use of some useful library types and the related idioms of Rust on the example of line data types. Information undoubtedly useful both for the beginning programmers on Rust, and for those that already managed to try a little this language, but yet not absolutely accustomed with rich library of types. The original post contains several inaccuracies and typographical errors in a code which I tried to correct in translation process, however in general the described approaches and motivation correct, suitable under concept "the best the practician", and therefore deserve attention.


In my last post we spoke about use much &str; as preferable type for the functions accepting line arguments. Closer to the end of a post we discussed when it is better to use String, and when &str; in structures (struct). Though I think that in general council is good, but in certain cases use &str; instead of String not optimum. For such cases we need other strategy.

Structure with String string fields


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

struct Person {
    name: String,
}

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

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

The example will earn, only if we do not forget about a challenge .to_string() in a method new() (Actually here it is better to use a method to_owned(), as method to_string() for placement of in-memory string uses quite heavy library of formatting of the text, and to_owned() just copies a line cut &str; directly in new object String — comment perev.). However, usability of function leaves much to be desired. If to use a string literal, then we can create new record Person so: Person::new("Herman"). But if we already have an owning line String, we need to receive the link to it:

let name = "Herman".to_string();
let person = Person::new(name.as_ref());

It seems that as if we beat about the bush. At first at us is String, then we cause as_ref() to turn it in &str;, only then to turn it back in String in a method new(). We could return to use String, it seems fn new(name: String) -> Person, but then to us it should force the user to cause constantly .to_string(), if that wants to create Person from a string literal.

Conversion by means of Into


We can make our function simpler in use by means of Into type. This type will automatically convert &str; in String. If we already have String, conversion will not be.

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());
}

Syntax of a signature new() now another is a little. We use the generalized types (English) and types (English) to explain Rust that some type S has to implement a type Into for type String. Type String implements Into<String> as empty operation, because String it is already held. Type &str; implements Into<String> with use of the same .to_string() (actually is not present — a comment perev.), which we used from the very beginning in a method new(). So we do not avoid need to cause .to_string(), and we clean need to do it to the user of a method. You can have a question whether use harms Into<String> productivities, and the answer — are not present. Rust uses static scheduling (English) and a monomorfization for processing of all parts in compilation time.

Such words as static scheduling or monomorfization can confuse a little you, but do not worry. Everything that you need to know, so is what the syntax shown above allows functions to accept and String, and &str;. If you think that fn new<S: Into<String>>(name: S) -> Person — very long syntax, yes, you are right. However, it is important to notice that in expression Into<String> there is nothing special. It just names of a type which is part of standard library Rust. You could write it if wanted. You can implement similar types if you count them rather useful and to publish on crates.io. All this power concentrated in a user code also does Rust by such delightful language.

Other method to write Person:: new ()


It is possible to use syntax of where which, perhaps, will read more simply, especially if the signature of function becomes more difficult:

struct Person {
    name: String,
}

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

What else to esteem



This article is a translation of the original post at habrahabr.ru/post/274455/
If you have any questions regarding the material covered in the article above, please, contact the original author of the post.
If you have any complaints about this article or you want this article to be deleted, please, drop an email here: sysmagazine.com@gmail.com.

We believe that the knowledge, which is available at the most popular Russian IT blog habrahabr.ru, should be accessed by everyone, even though it is poorly translated.
Shared knowledge makes the world better.
Best wishes.

comments powered by Disqus