You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
139 lines
4.6 KiB
Markdown
139 lines
4.6 KiB
Markdown
3 years ago
|
# Use borrowed types for arguments
|
||
|
|
||
|
## Description
|
||
|
|
||
3 years ago
|
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.
|
||
3 years ago
|
In this way, the function will accept more input types.
|
||
|
|
||
3 years ago
|
This is not limited to slice-able or fat pointer types.
|
||
1 year ago
|
In fact, you should always prefer using the **borrowed type** over
|
||
|
**borrowing the owned type**.
|
||
3 years ago
|
Such as `&str` over `&String`, `&[T]` over `&Vec<T>`, or `&T` over `&Box<T>`.
|
||
3 years ago
|
|
||
3 years ago
|
Using borrowed types you can avoid layers of indirection for those instances
|
||
|
where the owned type already provides a layer of indirection. For instance, a
|
||
|
`String` has a layer of indirection, so a `&String` will have two layers of
|
||
3 years ago
|
indirection. We can avoid this by using `&str` instead, and letting `&String`
|
||
3 years ago
|
coerce to a `&str` whenever the function is invoked.
|
||
3 years ago
|
|
||
|
## Example
|
||
|
|
||
3 years ago
|
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
|
||
2 years ago
|
`&Vec<T>` versus using a `&[T]` or using a `&Box<T>` versus a `&T`.
|
||
3 years ago
|
|
||
3 years ago
|
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.
|
||
3 years ago
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
fn main() {
|
||
|
let ferris = "Ferris".to_string();
|
||
|
let curious = "Curious".to_string();
|
||
|
println!("{}: {}", ferris, three_vowels(&ferris));
|
||
|
println!("{}: {}", curious, three_vowels(&curious));
|
||
|
|
||
|
// This works fine, but the following two lines would fail:
|
||
|
// println!("Ferris: {}", three_vowels("Ferris"));
|
||
|
// println!("Curious: {}", three_vowels("Curious"));
|
||
|
|
||
|
}
|
||
|
```
|
||
|
|
||
|
This works fine because we are passing a `&String` type as a parameter.
|
||
2 years ago
|
If we remove the comments on the last two lines, the example will fail. This
|
||
|
is because a `&str` type will not coerce to a `&String` type. We can fix this
|
||
|
by simply modifying the type for our argument.
|
||
3 years ago
|
|
||
|
For instance, if we change our function declaration to:
|
||
|
|
||
|
```rust, ignore
|
||
|
fn three_vowels(word: &str) -> bool {
|
||
|
```
|
||
|
|
||
|
then both versions will compile and print the same output.
|
||
|
|
||
|
```bash
|
||
|
Ferris: false
|
||
|
Curious: true
|
||
|
```
|
||
|
|
||
3 years ago
|
But wait, that's not all! There is more to this story.
|
||
3 years ago
|
It's likely that you may say to yourself: that doesn't matter, I will never be
|
||
|
using a `&'static str` as an input anyways (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`.
|
||
3 years ago
|
|
||
3 years ago
|
Let's now take an example where someone gives us a sentence, and we want to
|
||
3 years ago
|
determine if any of the words in the sentence contain three consecutive vowels.
|
||
3 years ago
|
We probably should make use of the function we have already defined and simply
|
||
|
feed in each word from the sentence.
|
||
3 years ago
|
|
||
|
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() {
|
||
3 years ago
|
let sentence_string =
|
||
3 years ago
|
"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);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
3 years ago
|
Running this example using our function declared with an argument type `&str`
|
||
|
will yield
|
||
3 years ago
|
|
||
|
```bash
|
||
|
curious has three consecutive vowels!
|
||
|
```
|
||
|
|
||
3 years ago
|
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.
|
||
3 years ago
|
|
||
|
## See also
|
||
3 years ago
|
|
||
3 years ago
|
- [Rust Language Reference on Type Coercions](https://doc.rust-lang.org/reference/type-coercions.html)
|
||
3 years ago
|
- 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
|