1

I'm new to rust and comes from a python domain, I've just started learning rust, so I've 2 questions to ask. 1) Why are we defining "String" again (name: String) when already I have mentioned the datatype to be as "String" in my "struct Person". 2) What exactly is the use of from. Please could someone explain me in simple english.

fn main() {
struct Person {
    name: String,
}

// instantiate Person struct
let person = Person {
    name: String::from("Steve Austin"), //Why are we defining "string" again and "from"
};

// access value of name field in Person struct
println!("Person name = {}", person.name);

}

tadman
  • 208,517
  • 23
  • 234
  • 262
fear_matrix
  • 4,912
  • 10
  • 44
  • 65

3 Answers3

4

In Rust there's two kinds of strings: str and String. The former is a really lean construct and is passed around as a reference, like &str. These cannot be modified.1 They also can't be copied, they're references, so they will always refer to the same value.

The reason they exist is because they are the "minimum viable string", they are the cheapest possible representation of textual data. This efficiency does have trade-offs.

A String can be modified if it's mut, and can also be copied, and later altered again. This makes them more suitable for properties that can and will change, or need to be computed at runtime.

Learning the difference here can be a bit bewildering if you're used to Python where strings are strings, but once you get a handle on it, you'll realize what's going on here.

"x" is a &str value, while String::from("x") is a String converted from that value. You can also do "x".into() if the type is well understood by the compiler, such as for a function argument or struct property.

Strings are also such a common thing that there's to_string() and to_owned(), both of which effectively do the same thing here on &str values.

If you want the best of both of these features, you can use Cow<str> which can encapsulate either an &str value, or a String, and you can convert from the "borrowed" value (&str) to an "owned" value via the to_owned() function.

These are more exotic, though, so I'd recommend only using them when you know what you're doing and need the performance gains they can offer.

--

1 Treat these like const char* in C++, versus std::string. The former is compiled into non-modifiable data in the executable, while the other uses a buffer that can be dynamically allocated.

tadman
  • 208,517
  • 23
  • 234
  • 262
4

Why are we defining "String" again (name: String) when already I have mentioned the datatype to be as "String" in my "struct Person". 2) What exactly is the use of from.

You're not defining anything, String::from is a static function (think classmethod), which converts to a string.

It's a bit like calling str() in Python.

The oddity here is that Rust has multiple string-adjacent types, and String is only one of them: an owned, heap-allocated, mutable, string buffer. Python doesn't really have an equivalent, StringIO is probably the closest relative (strarray would be if it existed, but it does not).

Meanwhile string literals are not owned heap-allocated strings, instead they are references to data stored in the rodata (or text) segment of the binary.

Python has nothing which really compares, because its strings are more or less always heap-allocated and created at runtime, they're just immutable. The closest general equivalent to &str would be a string version of memoryview, but it still lacks the lexical constraints, and the idea of a 'static lifetime.

For more, see

Masklinn
  • 34,759
  • 3
  • 38
  • 57
  • So correct me if I am wrong, basically from what I have understood, when we are defining "name: String" in my struct then we are saying that only string should come here. But when we are doing this " name: String::from("Steve Austin")", then we are saying that allocate heap memory to string with value name(Steve Austin). "::from" is just assigning values from right to left. Right? – fear_matrix Oct 09 '22 at 11:47
  • Kinda. In Python it's like defining a dataclass versus initialising it. ""::from" is just assigning values from right to left. Right?" from is just a function call no different from any other. But in struct initialisation (expression), `name:` is saying that you're assigning the RHS value to that name, in the struct, rather than a definition. In Python syntax it'd be something like `Person(name=str.from("Steve Austin")`. – Masklinn Oct 09 '22 at 14:13
  • Got it @Masklinn & I really appreciate you in connecting my issue with python syntax. – fear_matrix Oct 09 '22 at 18:41
  • maybe this can help you understand it a bit better: https://stackoverflow.com/questions/24158114/ – belst Oct 10 '22 at 08:51
1

In your example String::from() is not needed at all, it will work just fine without:

struct Person<'a> {
  name: &'a str
}
fn main() {
  let person = Person {
    name: "Steve Austin"
  };
  println!("Person name = {}", person.name);
}

But it really depends on if your program would ever need to change Person.name and if you think that the struct should hold the data itself ("own" the data).

A str is a fixed string, it is stored in your executable and can be referenced in your program with a pointer & as a &str pointer. But it can not be modified. In your case, "Steve Austin" can be used in your program as a &str.

In your example the name property is of type String. The contents of a String can be modified by your program because it lives in its own place in memory. This is where ownership comes in.

Rust will make sure that the String which is held by the struct lives long enough, by implicitly deriving both the lifetime of the struct and that of the String. Notice that to Rust, those are two different things. In your example, the struct really holds the data itself and so there is no question about what the lengths of the lifetimes should be.

In my example, Rust needs to be told the separate lifetimes for both the struct and the &str. Rust needs the &str to live (be present in memory) at least as long as the struct, because of the memory safety Rust offers via its borrow checker. This way the struct can not have a &str pointing to invalid memory (something that is not under control).

At first you might think that String in Rust is a pointer. But that is not the case. To Rust, String represents the data itself. When you want to reference that data, with a promise not to make modifications to it, you use a & (pointer). If you do want to make changes, you need to tell Rust your intention, and use a &mut (pointer to mutable data).

The difference of str and String in Rust will give you precise control over you program, and performance.

Code4R7
  • 2,600
  • 1
  • 19
  • 42