Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use coercion for arguments #29

Merged
merged 30 commits into from
Jan 4, 2021
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
302f5b4
Create str-vs-string section
cbreeden Sep 23, 2016
3c12bc8
correct spelling of vowel
cbreeden Sep 24, 2016
0a478cc
fix more typos
cbreeden Sep 24, 2016
4aed435
Update str-vs-string.md
cbreeden Sep 24, 2016
a38e09e
Add String vs Str link to readme
cbreeden Sep 24, 2016
03caded
Fix typos
cbreeden Sep 24, 2016
d6b6986
change title
cbreeden Oct 8, 2016
86dcfd4
Generalize the str vs string idiom
cbreeden Oct 8, 2016
366db72
Changing title to Use
simonsan Jan 1, 2021
048c704
Remove unnecessary whitespace
simonsan Jan 1, 2021
cd9fbf0
Added to SUMMARY.md
simonsan Jan 1, 2021
9c1a19b
Fixes from review
simonsan Jan 1, 2021
bf45e6c
Another fix from review
simonsan Jan 1, 2021
783e9fc
fix from older review
simonsan Jan 1, 2021
ad68802
More fixes from review
simonsan Jan 1, 2021
252f8f1
Merge branch 'master' into cbreeden-str-vs-string
simonsan Jan 2, 2021
4a05a94
Merge branch 'master' into cbreeden-str-vs-string
simonsan Jan 3, 2021
012addb
Update idioms/coercion-arguments.md
simonsan Jan 3, 2021
66aed67
Making tests run successfully
simonsan Jan 3, 2021
d06c86b
Small restructure
simonsan Jan 3, 2021
c9057b7
Update idioms/coercion-arguments.md
simonsan Jan 3, 2021
87985ca
Update idioms/coercion-arguments.md
simonsan Jan 3, 2021
e32db7d
Clear out testing of examples
simonsan Jan 3, 2021
268a6d3
Merge branch 'master' into cbreeden-str-vs-string
simonsan Jan 3, 2021
303bdba
Update idioms/coercion-arguments.md
simonsan Jan 4, 2021
3b161c0
Update idioms/coercion-arguments.md
simonsan Jan 4, 2021
45360aa
Update idioms/coercion-arguments.md
simonsan Jan 4, 2021
136b9b0
Update idioms/coercion-arguments.md
simonsan Jan 4, 2021
88d418c
Update idioms/coercion-arguments.md
simonsan Jan 4, 2021
f2266ba
Merge branch 'master' into cbreeden-str-vs-string
simonsan Jan 4, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- [Introduction](./intro.md)

- [Idioms](./idioms/index.md)
- [Use borrowed types for arguments](./idioms/coercion-arguments.md)
- [Concatenating Strings with `format!`](./idioms/concat-format.md)
- [Constructor](./idioms/ctor.md)
- [The `Default` Trait](./idioms/default.md)
Expand Down
115 changes: 115 additions & 0 deletions idioms/coercion-arguments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Use borrowed types for arguments

## Description

Using a target of a deref coercion can increase the flexibility of your code when you are deciding which argument type to use for a function argument.
In this way, the function will accept more input types.

For example; using `&str` instead of a `&String`, or `&[T]` in preference of `&Vec<T>`. This is not limited to slice-able or fat pointer types. In fact you should always prefer using the __borrowed type__ over __borrowing the owned type__, e.g., `&T` to `&Box<T>`.
MarcoIeni marked this conversation as resolved.
Show resolved Hide resolved
simonsan marked this conversation as resolved.
Show resolved Hide resolved

Using borrowed types you can avoid layers of indirection for those instances where the owned type already provides a layer of indirection, as it will be illustrated in the following examples. For instance, a `String` has a layer of indirection, so a `&String` will have two layers of indrection.
simonsan marked this conversation as resolved.
Show resolved Hide resolved
We can avoid this by using `&str` instead, and letting `&String` coerce to a `&str` whenever the function is invoked.

## Example

For this example, we will illustrate some differences for using `&String` as a function argument versus using a `&str`, but the ideas apply as well to using `&Vec<T>` versus using a `&[T]` or using a `&T` versus a `&Box<T>`.

Consider an example where we wish to determine if a word contains three consecutive vowels.
We don't need to own the string to determine this, so we will take a reference.

The code might look something like this:

```rust
fn three_vowels(word: &String) -> bool {
let mut vowel_count = 0;
for c in word.chars() {
match c {
'a' | 'e' | 'i' | 'o' | 'u' => {
vowel_count += 1;
if vowel_count >= 3 {
return true
}
}
_ => vowel_count = 0
}
}
false
simonsan marked this conversation as resolved.
Show resolved Hide resolved
}

fn main() {
let ferris = "Ferris".to_string();
let curious = "Curious".to_string();
println!("{}: {}", ferris, three_vowels(&ferris));
println!("{}: {}", curious, three_vowels(&curious));

// Using a `&String` type in our arguent we will find the following example fails:
// println!("Ferris: {}", three_vowels("Ferris"));
// println!("Curious: {}", three_vowels("Curious"));

}
```
MarcoIeni marked this conversation as resolved.
Show resolved Hide resolved

This example fails because a `&str` type will not coerce to a `&String` type.
We can fix this by simply modifying the type for our argument. For instance, if we change our function declaration to:

```rust, ignore
fn three_vowels(word: &str) -> bool {
```

then both of the previous examples will compile and print the same output.


```bash
Ferris: false
Curious: true
```

But wait, that's not all! There is more to this story.
It's likely that you may say to yourself: that doesn't matter, I will never be using a `&'static str` as an input anways (as we did when we used `"Ferris"`).
Even ignoring this special example, you may still find that using `&str` will give you more flexibility than using a `&String`.

Let's now take an example where someone gives us a sentence, and we want to determine if any of the words in the sentence has a word that contains three consecutive vowels.
We probably should make use of the function we have already defined and simply feed in each word from the sentence.

An example of this could look like this:

```rust
fn three_vowels(word: &str) -> bool {
let mut vowel_count = 0;
for c in word.chars() {
match c {
'a' | 'e' | 'i' | 'o' | 'u' => {
vowel_count += 1;
if vowel_count >= 3 {
return true
}
}
_ => vowel_count = 0
}
}
false
}

fn main() {
let sentence_string =
"Once upon a time, there was a friendly curious crab named Ferris".to_string();
for word in sentence_string.split(' ') {
if three_vowels(word) {
println!("{} has three consecutive vowels!", word);
}
}
}
```

Running this example using our function declared with an argument type `&str` will yield

```bash
curious has three consecutive vowels!
```

However, this example will not run when our function is declared with an argument type `&String`.
This is because string slices are a `&str` and not a `&String` which would require an allocation to be converted to `&String` which is not implicit, whereas converting from `String` to `&str` is cheap and implicit.

simonsan marked this conversation as resolved.
Show resolved Hide resolved
## See also

For more discussion on how to handle `String` and `&str` see [this blog series (2015)](https://web.archive.org/web/20201112023149/https://hermanradtke.com/2015/05/03/string-vs-str-in-rust-functions.html) by Herman J. Radtke III.
simonsan marked this conversation as resolved.
Show resolved Hide resolved