CSV from_writer works on stdout(), but fails on from_path - rust

Rust beginner here.
I've been trying to learn the CSV crate but got stuck on the following case.
My goal is to:
Parse a nested array
Set column names to array values
Write to CSV
Firstly here is the code that outputs exactly what I want it to.
use serde::Serialize;
use serde::Deserialize;
use csv;
use serde_json;
use std::io;
#[derive(Debug,Serialize,Deserialize)]
#[serde(transparent)]
struct Parent {
arr_field: Vec<Row>
}
#[derive(Debug,Serialize,Deserialize)]
struct Row {
a: u8,
b: u8,
c: u8,
}
fn main() {
let resp = r#" [[1,2,3],[3,2,1],[4,5,6]] "#;
let mut wtr = csv::WriterBuilder::new().from_writer(io::stdout());
let v: Parent = serde_json::from_str(resp).unwrap();
for row in v.arr_field{
wtr.serialize(row);
}
}
The output of this code is:
a,b,c
1,2,3
3,2,1
4,5,6
But when I want to save the output to a local file rather than stdout, like so:
let mut wtr = csv::WriterBuilder::new().from_path("./foo.csv");
I'm getting the following error at wtr.serialize
error[E0599]: no method named `serialize` found for enum `std::result::Result<Writer<File>, csv::Error>` in the current scope
Thank you for your help.

The error message tells you all you need to know - from_path returns a Result rather than a WriterBuilder, because opening that file might not always work. That is different with from_writer - no file needs to be opened, so no possibility of encountering an error.
To fix this, you can just use .unwrap(), like you do with serde_json::from_str the line below. This will cause a panic when an error was encountered, immediately terminating your program.
let mut wtr = csv::WriterBuilder::new().from_path("./foo.csv").unwrap();
Note that serialize also returns a result, so you should also add .unwrap() or some other logic to handle errors in your for loop. Rust will likely show a warning that there is an unused result.

Related

"found struct `ThereIsNoIteratorInRepetition`" when trying to repeat over a vector using `quote!`

I'm trying to create a Vec of TokenStreams, and then use that list in another quote! macro:
let list: Vec<_> = some_data
.iter()
.map(
|item| {
quote!{/*...*/}
},
)
.collect();
let generated = quote! {
fn hello_world() {
#(list);*
}
};
However, when compiling this, I'm getting this error:
expected struct `HasIterator`, found struct `ThereIsNoIteratorInRepetition`
From the macro's documentation, it seems that TokenStream should be valid in an interpolation, since it implements the ToTokens trait. Also, the list is a Vec, which is also explicitly allowed to be used in the loop interpolation.
Why am I getting the ThereIsNoIteratorInRepetition error when I am clearly using a valid iterator?
#(list);*
Should be
#(#list);*
I missed the inner interpolation # in the repetition interpolation, and it was driving me crazy for hours. Leaving this here in case anybody runs into the same thing.
I guess ThereIsNoIteratorInRepetition means that no interpolation was found in the repetition, when I originally thought it meant that the interpolation was parsed correctly, but was not accepted as an iterator.

Can you make an external iterator interface for this internal iterator?

I am using "external" and "internal" following this article. Say I am writing multi-column text rendering library: Each column should write its next line to the output in a round-robin fashion. I want to write a trait for columns (e.g. to have columns with different justification) such that this round-robin writing is ensured.
It is not hard to write this as an "internal" iterator that gives or "pushes" the writer for the next line to the columns, see the example below. However, it would be nicer to present an "external" iterator that lets each column "pull" all the line writers it needs.
struct LineWriter;
trait Column {
// With an API similar to this one, I can guarantee that columns write to
// lines in the correct order (by adding a return value that proves that the
// line was written, but I omitted that for simplicity).
fn write_to_line(&self, line: &mut LineWriter);
// But I would prefer an interface similar to this for Columns:
// fn write(&self, it: impl Iterator<Item=LineWriter>);
}
struct SomeColumn;
impl Column for SomeColumn {
fn write_to_line(&self, line: &mut LineWriter) { todo!() }
}
fn main() {
let cols = vec![SomeColumn, SomeColumn];
for line_number in 0..100 {
let mut line_writer = LineWriter;
for w in &cols {
w.write_to_line(&mut line_writer);
}
}
// In the preferred version, this would become
// for w in &cols {
// let line_writer_it = ??? to be determined ???
// w.write(line_writer_it);
// }
}
Playground
If we can control how columns are called (i.e. main() in this example) and what the LineWriter struct or its iterator look like, is there a way to have an internal iterator interface that has this guarantee? I think writing to other lines would need to happen in the iterator's next() function. But thinking along these lines, I got stuck because it would only ever to go deeper in the call stack, and never return from next() – how could it resume with writing the second line of the first column after writing the first line of the second column?. Perhaps the solution involves an iterator of impl Future<LineWriter>? Of course buffering the lines as Vec<String> or so and later writing them in an interleaved fashion is possible, but not what I'm interested in.

Unknown size at compile time when trying to print string contents in Rust

I have a couple of pieces of code, once errors out and the other doesn't, and I don't understand why.
The one that errors out when compiling:
fn main() {
let s1 = String::from("hello");
println!("{}", *s1);
}
This throws: doesn't have a size known at compile-time, on the line println!("{}", *s1);
The one that works:
fn main() {
let s1 = String::from("hello");
print_string(&s1);
}
fn print_string(s1: &String) {
println!("{}", *s1);
}
Why is this happening? Aren't both correct ways to access the string contents and printing them?
In the first snippet you’re dereferencing a String. This yields an str which is a dynamically sized type (sometimes called unsized types in older texts). DSTs are somewhat difficult to use directly
In the second snippet you’re dereferencing a &String, which yields a regular String, which is a normal sized type.
In both cases the dereference is completely useless, why are you even using one?

Is this the right way to read lines from file and split them into words in Rust?

Editor's note: This code example is from a version of Rust prior to 1.0 and is not syntactically valid Rust 1.0 code. Updated versions of this code produce different errors, but the answers still contain valuable information.
I've implemented the following method to return me the words from a file in a 2 dimensional data structure:
fn read_terms() -> Vec<Vec<String>> {
let path = Path::new("terms.txt");
let mut file = BufferedReader::new(File::open(&path));
return file.lines().map(|x| x.unwrap().as_slice().words().map(|x| x.to_string()).collect()).collect();
}
Is this the right, idiomatic and efficient way in Rust? I'm wondering if collect() needs to be called so often and whether it's necessary to call to_string() here to allocate memory. Maybe the return type should be defined differently to be more idiomatic and efficient?
There is a shorter and more readable way of getting words from a text file.
use std::io::{BufRead, BufReader};
use std::fs::File;
let reader = BufReader::new(File::open("file.txt").expect("Cannot open file.txt"));
for line in reader.lines() {
for word in line.unwrap().split_whitespace() {
println!("word '{}'", word);
}
}
You could instead read the entire file as a single String and then build a structure of references that points to the words inside:
use std::io::{self, Read};
use std::fs::File;
fn filename_to_string(s: &str) -> io::Result<String> {
let mut file = File::open(s)?;
let mut s = String::new();
file.read_to_string(&mut s)?;
Ok(s)
}
fn words_by_line<'a>(s: &'a str) -> Vec<Vec<&'a str>> {
s.lines().map(|line| {
line.split_whitespace().collect()
}).collect()
}
fn example_use() {
let whole_file = filename_to_string("terms.txt").unwrap();
let wbyl = words_by_line(&whole_file);
println!("{:?}", wbyl)
}
This will read the file with less overhead because it can slurp it into a single buffer, whereas reading lines with BufReader implies a lot of copying and allocating, first into the buffer inside BufReader, and then into a newly allocated String for each line, and then into a newly allocated the String for each word. It will also use less memory, because the single large String and vectors of references are more compact than many individual Strings.
A drawback is that you can't directly return the structure of references, because it can't live past the stack frame the holds the single large String. In example_use above, we have to put the large String into a let in order to call words_by_line. It is possible to get around this with unsafe code and wrapping the String and references in a private struct, but that is much more complicated.

Rust 0.9 -- Reading a file?

Here's what I'm trying to do: open all the command line arguments as (binary) files and read bytes from them. The constantly changing syntax here is not conductive to googling, but here's what I've figured out so far:
use std::io::{File, result};
use std::path::Path;
use std::os;
fn main() {
let args = os::args();
let mut iter = args.iter().skip(1); // skip the program name
for file_name in iter {
println(*file_name);
let path = &Path::new(*file_name);
let file = File::open(path);
}
}
Here's the issue:
test.rs:44:31: 44:41 error: cannot move out of dereference of & pointer
test.rs:44 let path = &Path::new(*file_name);
I've hit a brick wall here because while I'm fine with pointers in C, my understanding of the different pointer types in rust is practically non-existent. What can I do here?
Try &Path::new(file_name.as_slice())
Unfortunately, due to the trait argument that Path::new() takes, if you pass it a ~str or ~[u8] it will try and consume that type directly. And that's what you're passing with *file_name. Except you can't move out of a pointer dereference in Rust, which is why you're getting the error.
By using file_name.as_slice() instead (which is equivalent, in this case, to (*file_name).as_slice(), but Rust will do the dereference for you) it will convert the ~str to a &str, which can then be passed to Path::new() without a problem.

Resources