pub fn build_proverb(list: &[&str]) -> String {
let mut solution = String::new();
for i in 0 .. list.len() {
let current = list.get(i).unwrap() ;
let next = list.get(i + 1);
match next {
Some(v) => {
let string = format!("For want of a {} the {} was lost.\n", current, v);
solution.push_str(&string) }
None => {
let string = format!("And all for the want of a {}.", list.get(0).unwrap());
solution.push_str(&string)
}
}
}
solution
}
use std::fmt::Write;
pub fn build_proverb(list: &[&str]) -> String {
let mut solution = String::new();
for obj in list.windows(2) {
let _ = write!(solution, "For want of a {} the {} was lost.\n", obj[0], obj[1]);
}
let _ = write!(solution, "And all for the want of a {}.", list[0]);
solution
}
Notes:
write! avoids allocating new strings all the time
in general, when not used with strings, it can return errors, so let _ avoids the compiler warning about it
list.get(x).unwrap() is just &list[x], and the reference can be omitted since list already contains references.
Thanks for showing this.
I have to find out what windows does exactly. Never seen it before in my first days of learning rust.
and your code fails here :
test test_zero_pieces ... FAILED
failures:
---- test_zero_pieces stdout ----
thread 'test_zero_pieces' panicked at 'index out of bounds: the len is 0 but the index is 0', src\lib.rs:8:63
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
#[test]
fn test_zero_pieces() {
let input: Vec<&str> = vec![];
let expected = String::new();
assert_eq!(build_proverb(&input), expected);
}
windows gives you an iterator over successive "windows into" the slice, i.e.
| a | b | c | d |
1st: ^^^^^^^^^
2nd: ^^^^^^^^^
3rd: ^^^^^^^^^
It is often helpful when you have to work on items pairwise or n-wise. Also comes with a sibling called chunks that gives you non-overlapping subslices.
As for the test failure - adapting the code to fit your specification is left to you but I'd look at the first() method of slices.
Please don't teach to ignore errors. Yes, write!() to String never returns error, but it also means if we .unwrap() it will optimized out. Imagine in some time later OP changed solution's type to, say, ArrayString for some performance reason. Obviously write!() to ArrayString can fail, but it will be hard time to debug it if you silently ignored the error.
pub fn build_proverb(list: &[&str]) -> String {
let mut solution = String::new();
match list.slice(0,2).first() {
Some(v) => {
for obj in list.windows(2) {
let _ = write!(solution, "For want of a {} the {} was lost.\n", obj[0], obj[1]);
}}
None => return solution;
}
let _ = write!(solution, "And all for the want of a {}.", list[0]);
solution
}
pub fn build_proverb(list: &[&str]) -> String {
let mut solution = String::new();
match list.first() {
Some(_) => {
for obj in list.windows(2) {
let _ = write!(solution, "For want of a {} the {} was lost.\n", obj[0], obj[1]);
}
let _ = write!(solution, "And all for the want of a {}.", list[0]);
}
None => return solution
}
solution
}
oke, if I delete the let _ part I see this error message
unused `std::result::Result` that must be used
--> src\lib.rs:10:17
|
10 | write!(solution, "And all for the want of a {}.", list[0]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this `Result` may be an `Err` variant, which should be handled
Here is an entirely different solution, which avoids using indexes entirely, by using the split_first function and fold,
passing in the first element as the initial value for prev,
by returning s, it becomes the value for prev for the next iteration.
pub fn build_proverb(list: &[&str]) -> String {
let mut solution = String::new();
match list.split_first() {
Some((first, rest)) => {
rest.iter().fold(first, |prev, s| {
let string = format!("For want of a {} the {} was lost.\n", prev, s);
solution.push_str(&string);
s
});
let string = format!("And all for the want of a {}.", first);
solution.push_str(&string)
}
None => {
();
}
};
solution
}
The reason this happens is because you end up indexing into a slice of zero elements, therefore any index (Other than certain ranges) will not work.
What @birkenfeld was talking about .first() method was to use it to make sure we even have an element:
use std::fmt::Write;
pub fn build_proverb(list: &[&str]) -> String {
let mut solution = String::new();
for obj in list.windows(2) {
write!(solution, "For want of a {} the {} was lost.\n", obj[0], obj[1])
.expect("We should be able to write into a `String`");
}
//Here's the difference, we could or could not have a first element...
if let Some(x) = list.first() {
write!(solution, "And all for the want of a {}.", x)
.expect("We should be able to write into a `String`");
}
solution
}
This also incorporates the some error protection that was later discussed
sorry, Im now confused. How does now the code looks ?
if I try this :
pub fn build_proverb(list: &[&str]) -> String {
let mut solution = String::new();
for obj in list.windows(2) {
if let Some(x) = list.first() {
write!(solution, "For want of a {} the {} was lost.\n", obj[0], obj[1]);
}
write!(solution, "And all for the want of a {}.", list[0]);
}
solution
}
I see now 2 error messages
unused variable: `x`
--> src\lib.rs:6:21
| Finished dev [unoptimized + debuginfo] target(s) in 0.32s
6 | if let Some(x) = list.first() {
| ^ help: consider prefixing with an underscore: `_x`
|
= note: #[warn(unused_variables)] on by default
warning: unused `std::result::Result` that must be used
--> src\lib.rs:7:13
|
7 | write!(solution, "For want of a {} the {} was lost.\n", obj[0], obj[1]);
Those are Warnings, don't confuse them because one (Errors) means there's a type-level error and the other (Warnings) means that you probably made a logic-level error.
The thing here is that your call to first will return the first element in the case of there being one, which you take and check for. In the case that there is, you ignore the first element and... use the things in obj. That makes no sense, if I were to input something like this:
["Apple", "Banana", "Pear"]
You'd expect to get
For want of a Apple the Banana was lost.
For want of a Banana the Pear was lost.
And all for the want of a Apple.
But this will produce
For want of a Apple the Banana was lost.
And all for the want of a Apple.
For want of a Banana the Pear was lost.
And all for the want of a Apple.
You are interleaving the last sentence every time, it shouldn't be in the loop. Also, the first method call is meant to be for the very last sentence, not the body of the sentences you produce because it is possible that your input is empty, which won't break the iterator (The iterator will simply yield no items in the for loop) but it will break the last one which uses panicking indexing.
error[E0308]: mismatched types
--> src\lib.rs:8:6
|
8 | / match list.first(){
9 | | None => solution,
10 | | Some(v) => write!(solution, "And all for the want of a {}.", v);
11 | | }
| |______^ expected (), found struct `std::string::String`
|
= note: expected type `()`
found type `std::string::String`
on this code :
use std::fmt::Write;
pub fn build_proverb(list: &[&str]) -> String {
let mut solution = String::new();
for obj in list.windows(2) {
let _ = write!(solution, "For want of a {} the {} was lost.\n", obj[0], obj[1]);
}
match list.first(){
None => solution,
Some(v) => write!(solution, "And all for the want of a {}.", v);
}
solution
}
Compiling proverb v1.1.0 (C:\Users\roelof\Exercism\rust\proverb)
error[E0308]: match arms have incompatible types
--> src\lib.rs:10:21
|
8 | / match list.first(){
9 | | None => solution,
| | -------- this is found to be of type `std::string::String`
10 | | Some(v) => write!(solution, "And all for the want of a {}.", v),
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::string::String`, found enum `std::result::Result`
11 | | }
| |______- `match` arms have incompatible types
pub fn build_proverb(list: &[&str]) -> String {
let mut solution = String::new();
for obj in list.windows(2) {
let _ = write!(solution, "For want of a {} the {} was lost.\n", obj[0], obj[1]);
}
match list.first(){
None => solution,
Some(v) => write!(solution, "And all for the want of a {}.", v),
}
solution
}
use std::fmt::Write;
pub fn build_proverb(list: &[&str]) -> String {
let mut solution = String::new();
for obj in list.windows(2) {
let _ = write!(solution, "For want of a {} the {} was lost.\n", obj[0], obj[1]);
}
match list.first() {
None => solution,
Some(v) => {write!(solution, "And all for the want of a {}.", v)
.expect("We should be able to write into a `String`"); solution}
}
}