I have a function to reverse characters in words and I need to know how to convert a Vec<&String> into a regular owned String (with spaces between the words).
fn reverse_words(words: &str) -> String {
let mut split = words.split_whitespace();
let vec: Vec<&str> = split.collect();
let vec2 = vec!();
for word in vec {
vec2.push(&word.chars().rev().collect::<String>());
}
return vec2;
}
.
error[E0308]: mismatched types
--> src/lib.rs:8:12
|
1 | fn reverse_words(words: &str) -> String {
| ------ expected `String` because of return type
...
8 | return vec2;
| ^^^^ expected struct `String`, found struct `Vec`
|
= note: expected struct `String`
found struct `Vec<&String>`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `challenge` due to previous error
NOTE: Line numbers in error messages can be incorrect due to concatenation.
Here is a version of your code with minimal changes to make it work. You basically only need to add a .join(' ') to your last statement:
fn reverse_words(words: &str) -> String {
let split = words.split_whitespace();
let vec: Vec<&str> = split.collect();
let mut vec2 = vec![];
for word in vec {
vec2.push(word.chars().rev().collect::<String>());
}
vec2.join(" ")
}
fn main() {
let sentence = "hello world";
assert_eq!(reverse_words(sentence), "olleh dlrow");
}
Playground.
This is an inefficient implementation requiring a lot of allocation though. You can chain methods of the Iterator trait to get a solution that does not require as much allocation:
fn reverse_words(words: &str) -> String {
words
.split_whitespace()
.map(|w| w.chars().rev().chain([' ']))
.flatten()
.take(words.len())
.collect::<String>()
}
fn main() {
let sentence = "hello world";
assert_eq!(reverse_words(sentence), "olleh dlrow");
}
Playground.
Related
A simple program, what I am trying to do is get lines from a file, or if the file is not present, pass in a zero-length iterator:
use std::fs::File;
use std::io::{self, BufRead};
fn run_against_input(inp: &mut dyn Iterator<Item = String>) {
for i in inp {
println!("Input line: {}", i);
}
}
fn main() {
let file = File::open("data.txt");
let input: dyn Iterator<Item = String> = match file {
Ok(f) => io::BufReader::new(f).lines()
.map(|line| line.unwrap()),
Err(_) => Vec::new().into_iter()
};
run_against_input(&mut dyn input);
}
When I do this, I get the following error:
Compiling playground v0.0.1 (/playground)
error: expected expression, found keyword `dyn`
--> src/main.rs:19:28
|
19 | run_against_input(&mut dyn input);
| ^^^ expected expression
error[E0308]: mismatched types
--> src/main.rs:13:18
|
13 | Ok(f) => io::BufReader::new(f).lines()
| __________________^
14 | | .map(|line| line.unwrap()),
| |______________________________________^ expected trait object `dyn Iterator`, found struct `Map`
|
= note: expected trait object `dyn Iterator<Item = String>`
found struct `Map<std::io::Lines<BufReader<File>>, [closure#src/main.rs:14:18: 14:38]>`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to 2 previous errors
I do specifically need a &mut reference to the Iterator in the code I am writing (this is a small reproduction of the problem that I am facing), but I guess that has nothing to do with the issue at hand. I see that there is an impl on Iterator for Map, but the error I get is that this is not the trait object for Iterator<Item = String>. I also tried this:
let input: &mut dyn Iterator<Item = String> = match file {
Ok(f) => &mut io::BufReader::new(f).lines()
.map(|line| line.unwrap()),
Err(_) => &mut Vec::new().into_iter()
};
which of course didn't work, since the temporary value is being dropped in the statement to which I am returning a reference (which is why I did the let binding thing as the compiler suggested).
EDIT Link to playground - https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=99b8105ecc266e03db2d92cc22610962
You can hold the iterators for each branch in a separate variable outside the match (and also use std::iter::empty() instead of Vec::new().into_iter()):
use std::fs::File;
use std::io::{self, BufRead};
use std::iter;
fn run_against_input(inp: impl Iterator<Item = String>) {
for i in inp {
println!("Input line: {}", i);
}
}
fn main() {
let file = File::open("data.txt");
let mut lines;
let mut no_lines;
let input: &mut dyn Iterator<Item = String> = match file {
Ok(f) => {
lines = io::BufReader::new(f).lines().map(Result::unwrap);
&mut lines
},
Err(_) => {
no_lines = iter::empty();
&mut no_lines
},
};
run_against_input(input);
}
You can use a Box instead of a reference, so the value is owned and won't be dropped:
let mut input: Box<dyn Iterator<Item = String>> = match file {
Ok(f) => Box::new(io::BufReader::new(f).lines().map(|line| line.unwrap())),
Err(_) => Box::new(Vec::new().into_iter()),
};
playground
If you prefer you could also change your function to take a Box instead of a reference:
fn run_against_input(inp: Box<dyn Iterator<Item = String>>) {
I would like to create a sorted word from a String
Here is what I have so far.
fn sort_word(word: &str) -> String {
word.chars()
.collect::<Vec<char>>()
.sort_unstable()
.iter()
.collect()
}
I do not understand the compiler message :
error[E0599]: no method named `iter` found for unit type `()` in the current scope
--> src/lib.rs:5:10
|
5 | .iter()
| ^^^^ method not found in `()`
What am I doing wrong here?
In case it helps anyone
fn sort_word(word: &str) -> String {
let mut cvec = word.chars().collect::<Vec<_>>();
cvec.sort_unstable();
cvec.iter().collect()
}
Just for fun and to have another option, you could use a HashMap to count the items and then reconstruct the whole string.
Here is an example for ASCII characters:
use std::collections::HashMap;
use std::iter;
fn sort_string(s: &str) -> String {
let mut counter = HashMap::new();
for c in s.chars() {
*counter.entry(c).or_insert(0usize) += 1;
}
(0..=255)
.map(char::from)
.filter_map(|c| counter.get(&c).map(|n| iter::repeat(c).take(*n)))
.flatten()
.collect()
}
Playground
Currently my code looks like this:
use std::process::Command;
use std::str;
fn main() {
let result_version = Command::new("git")
.arg("--version")
.output()
.unwrap();
let rc_version = result_version.status;
println!("{}", rc_version);
let mut stdout_version = str::from_utf8(&result_version.stdout).unwrap().to_string();
let stdout_version_trimmed= trim_newline(stdout_version);
println!("'{}'", stdout_version_trimmed);
}
fn trim_newline(s: &mut String) {
if s.ends_with('\n') {
s.pop();
if s.ends_with('\r') {
s.pop();
}
}
}
But unfortunately the compiler gives me:
error[E0308]: mismatched types
--> src/main.rs:15:46
|
15 | let stdout_version_trimmed= trim_newline(stdout_version);
| ^^^^^^^^^^^^^^
| |
| expected `&mut std::string::String`, found struct `std::string::String`
| help: consider mutably borrowing here: `&mut stdout_version`
So after the comment of mcarton I realised that the trim_newline does not return the changed string it is just changing it.
So the final result is this which compiles error free without any warning.
use std::process::Command;
use std::str;
fn main() {
let result_version = Command::new("git")
.arg("--version")
.output()
.unwrap();
let rc_version = result_version.status;
println!("{}", rc_version);
let mut stdout_version = str::from_utf8(&result_version.stdout).unwrap().to_string();
trim_newline(&mut stdout_version);
println!("'{}'", stdout_version);
}
fn trim_newline(s: &mut String) {
if s.ends_with('\n') {
s.pop();
if s.ends_with('\r') {
s.pop();
}
}
}
I am trying to implement a new trait called AppendBar for String. Its only function is append_bar.
From my understanding, self should be an instance of a String.
trait AppendBar {
fn append_bar(self) -> Self;
}
impl AppendBar for String {
fn append_bar(self) -> Self{
self.clone().push_str("Bar")
}
}
fn main() {
let s = String::from("Foo");
let s = s.append_bar();
println!("s: {}", s); // "s: FooBar"
}
This is obviously not the case, because I receive the following error:
error[E0308]: mismatched types
--> exercises/traits/traits1.rs:18:9
|
17 | fn append_bar(self) -> Self{
| ---- expected `std::string::String` because of return type
18 | self.clone().push_str("Bar")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::string::String`, found `()`
Can anyone help me understand my misconception?
17 | fn append_bar(self) -> Self{
| ---- expected `std::string::String` because of return type
18 | self.clone().push_str("Bar")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::string::String`, found `()`
is saying that it expected append_bar to return a String, but self.clone().push_str("Bar") evaluates to a type of (), the unit type. The compiler error is correct, because the push_str function's type is fn push_str(&mut self, string: &str), note that it has no return type and instead mutates its Self argument.
Instead, you need to push into the string and then return the string, e.g.
impl AppendBar for String {
fn append_bar(mut self) -> Self{
self.push_str("Bar");
self
}
}
I've also remove the .clone() because it was not necessary. append_bar already accepts self and thus takes ownership of the string value, so you can push into it and return it without needing to clone it.
#loganfsmyth's answer explains why you get this error message. There are three ways to solve it depending on your expectations for append_bar:
Take ownership
If you expect append_bar to return the modified string and don't want the caller to be able to use the input string afterwards:
impl AppendBar for String {
fn append_bar (mut self) -> Self {
self.push_str ("Bar");
self
}
}
let s1 = String::from ("Foo");
let s2 = s1.append_bar();
// println!("s1: {}", s1); // Error: s1 is no longer usable at this point
println!("s2: {}", s2); // Prints "FooBar"
Playground
(This is the same solution as #loganfsmyth's answer).
Borrow and clone
If you expect append_bar to return the modified string and want the caller to be able to keep using the original input string afterwards:
impl AppendBar for String {
fn append_bar (&self) -> Self {
let mut s = self.clone();
s.push_str ("Bar");
s
}
}
let s1 = String::from ("Foo");
let s2 = s1.append_bar();
println!("s1: {}", s1); // Prints "Foo"
println!("s2: {}", s2); // Prints "FooBar"
Playground
Mutate in place
If you expect append_bar to replace the input with the modified string:
impl AppendBar for String {
fn append_bar (&mut self) {
self.push_str ("Bar");
}
}
let mut s1 = String::from ("Foo");
s1.append_bar();
println!("s1: {}", s1); // Prints "FooBar"
Playground
I'm on chapter 12 of The Rust Programming Language, where a case insensitive line search is implemented. It doesn't make sense to me to implement the same logic twice, so I figured if I just called the case sensitive search function with the parameters converted to lower case, that might work. It did not.
This is my non working code:
fn main() {
let a = search("Waldo", "where in\nthe world\nis Waldo?");
let b = search("waldo", "where in\nthe world\nis Waldo?");
let c = search_case_insensitive("waldo", "where in\nthe world\nis Waldo?");
println!("{:?}", a);
println!("{:?}", b);
println!("{:?}", c);
}
pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
let mut results = Vec::new();
for line in contents.lines() {
if line.contains(query) {
results.push(line);
}
}
results
}
pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
let query = query.to_lowercase();
let contents2: &str = &contents.to_lowercase();
search(&query, contents2)
}
The error in most versions I've come up with is inevitably something very much like:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:25:28
|
25 | let contents2: &str = &contents.to_lowercase();
| ^^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough
...
28 | }
| - temporary value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 23:1...
--> src/main.rs:23:1
|
23 | pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
EDIT 2:
Since you've updated the question with an MCVE and you've stated you don't care about straying away from the book examples... here is another version relying on extra allocations via the use of String:
fn main() {
let a = search("Waldo", "where in\nthe world\nis Waldo?");
let b = search("waldo", "where in\nthe world\nis Waldo?");
let c = search_case_insensitive("waldo", "where in\nthe world\nis Waldo?");
println!("{:?}", a);
println!("{:?}", b);
println!("{:?}", c);
}
pub fn search<S>(query: S, contents: S) -> Vec<String> where S: Into<String> {
let query = query.into();
let mut results = Vec::new();
for line in contents.into().lines() {
if line.contains(&query) {
results.push(line.into());
}
}
results
}
pub fn search_case_insensitive<S>(query: S, contents: S) -> Vec<String> where S: Into<String> {
let query = query.into().to_lowercase();
let contents = contents.into().to_lowercase();
search(query, contents)
}
Here it is running in the Playground
EDIT:
I realised I never really gave you an alternative. Here's what I would probably do:
pub enum SearchOptions {
CaseSensitive,
CaseInsensitive
}
pub fn search<'a>(query: &str, contents: &'a str, options: SearchOptions) -> Vec<&'a str> {
let mut results = Vec::new();
for line in contents.lines() {
let check = match options {
SearchOptions::CaseSensitive => line.contains(query),
SearchOptions::CaseInsensitive => line.to_lowercase().contains(&query.to_lowercase()),
};
if check {
results.push(line);
}
}
results
}
This is about as far as you could get "de-dupe"'ing it.
Original answer:
The actual problem is that you're trying to pass the contents around when its bound to the lifetime 'a ... but what you really want to be "case insensitive" is the query.
This isn't bound to the lifetime 'a in quite the same way and as such ... works:
pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
let query = query.to_lowercase();
search(&query, contents)
}
Here it is on the playground
You'll need to still duplicate the logic though ... because you need to match the lowercase query with the lowercase line ... which is demonstrated in the examples in the book:
if line.to_lowercase().contains(&query) {
// ^^^^^^^^^^^^^^ each LINE is converted to lowercase here in the insensitive search
results.push(line);
}
"How do I stop duplicating the logic?" - well they're not quite the same in the first place. I think your attempt wasn't quite what you were after in the first place (happy to be corrected though).
You have introduced an impossible constraint on the lifetime of variable contents2; by writing &'a you are attempting to assign to it the same lifetime as to the contents argument, but it is created and destroyed within the scope of search_case_insensitive and thus is outlived by contents.
In order for contents2 to outlive the body of search_case_insensitive you would need to either return it as a String and assign to some variable outside of it or pass it to search_case_insensitive by reference as long as it already exists as a String elsewhere.
Citing The Book:
It's important to understand that lifetime annotations are
descriptive, not prescriptive. This means that how long a reference is
valid is determined by the code, not by the annotations.