Listing 10-19: Be consistent in creating the two strings
The variables string1 and string2 are created and passed to longest in different ways for no apparent reason. I've made a small change to make Listing 10-19 consistent with the rest of the examples in Section 10.3.
Hello,
unfortunately I have the strong feeling that your PR is based on some misunderstanding and makes not that much sense.
The reason that we create functions in Rust with slice parameters &str is just that we then can pass owned strings of String type and slices of &str type. And I think the example tried to show that, in conjunction with other stuff to learn, e.g. lifetimes. So that the two parameters for longest() have different type is intended. If understanding the difference of the two string types is your problem, you might consult the official book again, or maybe study other books, e.g. https://rust-for-c-programmers.salewskis.de/ch8/8_5_slices_as_parameters_and_return_types.html#85-slices-as-parameters-and-return-types
If indeed only consistency is your desire: Do you really would like to add additional redundant code just to make it look more consistent? Most programming languages try to avoid too much unneeded code, because we have to type it in, and more importantly, we have to read it whenever we check the code. And Rust is already quite verbose.
Another question might be, why the book examples are using as_str() to pass a owned String as a &str instead of just passing the owned string just as a reference, as in the working example below:
fn main() {
let string1 = String::from("long string is long");
{
//let string2 = String::from("xyz");
let string2 = "xyz";
let result = longest(&string1, string2);
println!("The longest string is {result}");
}
}
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
And I think the example tried to show that, in conjunction with other stuff to learn, e.g. lifetimes. So that the two parameters for longest() have different type is intended.
I don't see why this would be intended in a section that is about lifetimes and not slices. As it is written right now, the example creates two strings differently, and upon first read I paused to think whether there is any significance to this difference. It turned out after finishing the section that there is no significance, which is also apparent from the fact that the difference is not commented on in the text and that later similar examples stick to a single way of creating and passing the strings.
If indeed only consistency is your desire: Do you really would like to add additional redundant code just to make it look more consistent?
The point here is not consistency for its own sake but improving the quality of writing.
Well, the book maintainers or maybe the original authors will decide if accepting your PR.
I strongly assume that the original authors had their reason to write it the way it is. You are right that the two string forms are not the core topic of that section, but it can make sense to repeat some topics from earlier chapters. (I assume the two string forms have been introduced earlier? If not, that would be indeed a bug.) But well, I am from Germany, I can imagine that teaching strategy is different in other countries, e.g. in some Asian or African nations.
I paused to think whether there is any significance to this difference.
Same for me, when I read it in October 2023. After a few seconds I remembered, that Rust has two string types, and both can be passed to functions with &str parameters. I still wonder a bit why as_str was used, instead of passing the owned String just as a reference, which seems to be more idiomatic today. Maybe it is just a legacy from old Rust days.
I think this one is probably just a strange legacy of the loooong history of the book (though I’m not sure! My history spelunking). I don’t think there’s any particular reason these need to be different types, and it’s also odd that it uses String::as_str instead of just passing a reference here. What is more, none of the examples in this section depend on the source being a String rather than a string slice &str. My inclination here would be that if we do anything, it is actually to convert them all to just being string literals, and passed directly to the longest function. If you’re up for making that change, I’ll see about getting it landed!
What is more, none of the examples in this section depend on the source being a String rather than a string slice &str. My inclination here would be that if we do anything, it is actually to convert them all to just being string literals, and passed directly to the longest function.
I think Listing 10-23 does rely on the source being a String, because it fails to demonstrate its point when the arguments to the longest function have 'static lifetime. If we convert everything to string literals, then the following example will compile, but for the purposes of Section 10.3 it should fail. Therefore, I'm guessing that using String was a deliberate choice in this section, although I could replace the uses of String::as_str by references if you wish.
fn main() {
let string1 = String::from("long string is long");
let result;
{
let string2 = String::from("xyz");
result = longest(string1.as_str(), string2.as_str());
}
println!("The longest string is {result}");
}
Ah, good catch. I wondered about that specific example, but my quick testing the other day missed that issue. I do think using &string1, &string2 (and so likewise throughout) is preferable, and given that it’s useful to use String there, that it’s probably least distracting to just use Strings rather than &strs throughout the section (in line with your initial approach). Thanks!
I've made the changes for the entire section. Let me know if something else needs to be done.