Writing to a file or stdout in Rust - file

I'm learning Rust, and I'm somewhat stumped.
I'm trying to give the user the option of writing output to stdout or to a supplied filename.
I started with the example code that's given for using extra::getopts located here. From there, in the do_work function, I'm trying to do this:
use std::io::stdio::stdout;
use std::io::buffered::BufferedWriter;
fn do_work( input: &str, out: Option<~str> ) {
println!( "Input: {}", input );
println!( "Output: {}", match out {
Some(x) => x,
None => ~"Using stdout"
} );
let out_writer = BufferedWriter::new( match out {
// I know that unwrap is frowned upon,
// but for now I don't want to deal with the Option.
Some(x) => File::create( &Path::new( x ) ).unwrap(),
None => stdout()
} );
out_writer.write( bytes!( "Test output\n" ) );
}
But it outputs the following error:
test.rs:25:43: 28:6 error: match arms have incompatible types: expected `std::io::fs::File` but found `std::io::stdio::StdWriter` (expected struct std::io::fs::File but found struct std::io::stdio::StdWriter)
test.rs:25 let out_writer = BufferedWriter::new( match out {
test.rs:26 Some(x) => File::create( &Path::new( x ) ).unwrap(),
test.rs:27 None => stdout()
test.rs:28 } );
test.rs:25:22: 25:41 error: failed to find an implementation of trait std::io::Writer for [type error]
test.rs:25 let out_writer = BufferedWriter::new( match out {
^~~~~~~~~~~~~~~~~~~
But I don't understand what the issue is because both File and StdWriter implement the Writer Trait. Can someone explain what I'm doing wrong?
Thanks!

A lot has changed in Rust since 2014, so here is an answer that works for me using Rust 1.15.1:
let out_writer = match out {
Some(x) => {
let path = Path::new(x);
Box::new(File::create(&path).unwrap()) as Box<dyn Write>
}
None => Box::new(io::stdout()) as Box<dyn Write>,
};
This is pretty much the same as #Arjan's answer, except that ~ was replaced by Box, and some names have changed. I'm leaving out BufferedWriter, but if you want that, I believe it is now named BufWriter.

Yes, both implement Write, but the problem is BufWriter is expecting a type T that implements Writer, and that T can't be File and Stdout at the same time.
You must cast both to the common type (either Box<dyn Write> or &dyn Write, but since you cannot return references you have to use Box):
fn do_work(input: &str, out: Option<String>) {
let mut out_writer: Box<dyn Write> = BufWriter::new(match out {
Some(ref x) => Box::new(File::create(&Path::new(x)).unwrap()),
None => Box::new(stdout()),
});
out_writer.write(b"Test output\n").unwrap();
}
You should also handle errors properly, not just using unwrap (used in example for simplicity).

Related

Rust Programming - Trying to compare user input to lines from a file

So I am trying to compare user input to the lines from a separate file name fruits.txt. I got it mostly working I believe, but I am running into this error:
error[E0658]: use of unstable library feature 'option_result_contains'
--> src/main.rs:19:20
|
19 | s if s.contains(&ask) => println!("{} is a fruit!", ask),
| ^^^^^^^^
|
= note: see issue #62358 <https://github.com/rust-lang/rust/issues/62358> for more information
For more information about this error, try `rustc --explain E0658`.
error: could not compile `learn_arrays` due to previous error
I have tried several types of ways to match it in rust and this is the closest where it doesn't complain that I am trying to match a string to whatever type lines is. here is what it looks like
use std::fs::File;
use std::io::{BufReader, BufRead, Error, stdin};
fn main() -> Result<(), Error>{
let path = "fruits.txt";
let input = File::open(path)?;
let buffered = BufReader::new(input);
let mut ask = String::new();
stdin()
.read_line(&mut ask)
.expect("Failed to read line");
let ask: String = ask.trim().parse().expect("Please type a valid string!");
for line in buffered.lines() {
match line {
s if s.contains(&ask) => println!("{} is a fruit!", ask),
_ => println!("{} is either not in the list or not a fruit", ask),
}
}
Ok(())
}
Is there a way where I can use the unstable feature or is there another better method to compare user input to lines from a file.
I was able to fix the issue my changing the part where I am attempting to match the input with:
let mut found = false;
println!("Result");
for line in buffered.lines() {
let s = line.unwrap();
if s.find(&ask).is_some() {
println!("{} is a fruit!", ask);
found = true;
break;
}
}
if !found {
println!("{} is either not in the list or not a fruit", ask)
}

What is an idiomatic way of looping with a match when I only care about one variant and not even the value in the variant?

loop {
match stream.write(&buffer) {
Ok(_) => break,
_ => {}
}
}
Is there a way to write it on a more readable/idiomatic way? I don't need to do anything inside the match, I don't need any actions after the Ok or after the Err, because the buffer is updated as a reference whenever it returns Ok.
Something like:
while match some_fn() != Ok() {
...
}
The general case is covered by ForceBru's answer, but since you have a Result and don't care about the value inside Ok, you can use Result::is_ok (or Result::is_err):
while stream.write(&buffer).is_err() {}
As pointed out by rodrigo in the comments:
Worth noting that Rust 1.42 adds the matches! macro, so you can write while matches!(stream.write(&buffer), Ok(_)) {}, useful for other enums that lack the is_* functions.
You can use while let:
fn some_fn(x: u32) -> Option<u32> {
if x > 30 {
Some(x)
} else {
None
}
}
fn main() {
let mut x = 0;
while let None = some_fn(x) {
x += 1;
}
dbg!(x);
}
Playground
Same with the Result type: check if some_fn returned Err.

'withUnsafeBytes' is deprecated warning when passing void* argument to c function in swift 5

I have a library parsing FIT file in swift using an externally provided c library. The parsing function takes as argument a void * data.
To call the function, I was converting the data using data.withUnsafeBytes( { (ptr: UnsafePointer<UInt8>) in ...} to build the argument to the c function and it was working fine.
After the upgrade of Xcode to swift 5, I now get a deprecated warning
'withUnsafeBytes' is deprecated: use withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R instead
I couldn't work out how to fix the code to remove the deprecated warning. The code has been working fine and without warning in swift 4
I tried to change the argument in the closure to take UnsafeRawBufferPointer instead of the UnsafePointer but this resulted in an error in calling the function: Cannot convert 'UnsafeRawBufferPointer' to expected argument type 'UnsafeRawPointer?'
This is a small swift file to show the problem:
import Foundation
// Create sample data (Typically would be read from a file
let data = Data(repeating: 1, count: 10)
data.withUnsafeBytes( { (ptr : UnsafePointer<UInt8>) in
// call the c function with the void* argument
let value = readFITfile(ptr)
print( value )
})
And an example c function
unsigned readFITfile(const void * data){
//Silly example to show it works, just returning the value of pointer as int
//Typically would parse the data and return a structure
return (unsigned)data;
}
I saved a small repo with the above code here https://github.com/roznet/swift2c and the full scale project with the parsing of the file is here https://github.com/roznet/fit-sdk-swift
You have to change the closure argument to UnsafeRawBufferPointer and then take its baseAdress (which is a UnsafeRawPointer?, the Swift equivalent of void * in C):
data.withUnsafeBytes( { (ptr : UnsafeRawBufferPointer) in
let value = readFITfile(ptr.baseAddress)
// ...
})
The Swift compiler can also infer the closure argument type automatically:
data.withUnsafeBytes( { ptr in
let value = readFITfile(ptr.baseAddress)
// ...
})
For more information about this problem, see withUnsafeBytes Data API confusion in the Swift forum.
To get UnsafePointer now you should do something like that
data.withUnsafeBytes { (ptr: UnsafeRawBufferPointer) in
if let ptrAddress = ptr.baseAddress, ptr.count > 0 {
let pointer = ptrAddress.assumingMemoryBound(to: UInt8.self) // here you got UnsafePointer<UInt8>
let value = readFITfile(ptr)
print( value )
} else {
// Here you should provide some error handling if you want ofc
}
}

How to implement a vector (array, ..) of functions in Rust when the functions come from different modules

I have seen:
Rust array of functions
Iterate over vector of functions
and searched online. I do not want closures. I am trying to implement a classic dynamic(-ish) function lookup table.
mod impl_foo;
mod impl_bar;
use utils;
// a CmdResult is a Result with a tuple of an int and a string
static FUNCTIONS: &'static [fn(&[String]) -> utils::CmdResult] = &[
("bar".to_string(), impl_bar::call_bar),
("foo".to_string(), impl_foo::call),
];
fn find_action(name: &String) -> (fn(&[String]) -> utils::CmdResult) {
match FUNCTIONS.binary_search_by(|item| item[0].cmp(name)) {
Ok(action) => action,
Err(_) => (|&[String]| Err((1, format!("Unknown '{}'", name))))
}
}
// later on in another function ....
action = find_action("foo");
let result = action(args);
// process results
But this does not compile:
src/main.rs:44:5: 44:50 error: mismatched types:
expected `fn(&[collections::string::String]) -> core::result::Result<i32, (i32, collections::string::String)>`,
found `(collections::string::String, fn(&[collections::string::String]) -> core::result::Result<i32, (i32, collections::string::String)> {impl_foo::call})`
and again for impl_bar::call_bar.
What am I missing? It appears to have something to do with the use of different modules since it clearly works for other people.
I also tried to define a type:
type Action = fn(&[String]) -> utils::CmdResult;
and use that to cut down on typing but no luck there either.
BTW, you need #![feature(slice_patterns)] because of the &[String].
Edit the next morning.....
As Francis points out below my transcription here had a flaw. It did not exactly match the real problem I had but it helped me see with fresh eyes.
The slice pattern requirement is because I was trying to handle unknown functions with a closure. Once I removed that the complaint went away. I was trying to be a little too dynamic language style I think :-)
Below is the completed code that actually works so that people finding this question can see working code.
type Action = fn(&[String]) -> utils::CmdResult;
static FUNCTIONS: &'static [(&'static str, Action)] = &[
("bar", impl_bar::call),
("foo", impl_foo::call_foo),
];
fn find_action(prog: &String) -> Option<Action> {
match FUNCTIONS.binary_search_by(|&(name,_)| name.cmp(prog)) {
Ok(idx) => Some(FUNCTIONS[idx].1),
Err(_) => None,
}
}
fn invoke(prog: &String, args: &[String]) -> i32 {
let result = match find_action(prog) {
Some(action) => action(args),
None => Err((1, format!("Unknown: {}", prog))),
};
result.unwrap_or_else(|(n, msg)| {
writeln!(io::stderr(), "{}", msg).ok();
n
})
}
Read the error message carefully:
src/main.rs:44:5: 44:50 error: mismatched types:
expected `fn(&[collections::string::String]) -> core::result::Result<i32, (i32, collections::string::String)>`,
found `(collections::string::String, fn(&[collections::string::String]) -> core::result::Result<i32, (i32, collections::string::String)> {impl_foo::call})`
Let's simplify it:
src/main.rs:44:5: 44:50 error: mismatched types:
expected `fn(&[String]) -> Result<i32, (i32, String)>`,
found `(String, fn(&[String]) -> Result<i32, (i32, String)> {impl_foo::call})`
What this message is telling you is that you're trying to put a tuple of String and a function type into an array that expects only the function type.
You probably meant to define your array like this:
static FUNCTIONS: &'static [(&'static str, fn(&[String]) -> utils::CmdResult]) = &[
("bar", impl_bar::call_bar),
("foo", impl_foo::call),
];

Get file descriptor from `std::io::File::open`

I am writing binding for a C library, I'd like to call std::io::File::open as it's got error handling already. I then intend to pass the fd to C function.
I have looked at std::io::fs, but the fd field is nothing like what I would have thought.
After some more digging I found native::io::file::FileDesc, which indeed has fn fd(&self) -> fd_t, but doesn't seem like this is something I can access from an instance of std::io::File.
There appear to be fs_from_raw_fd method, it's the exact opposite to what I need.
use std::os::unix::io::AsRawFd;
let path = Path::new("my.txt");
let display = path.display();
let mut file = match File::open(&path) {
// The `description` method of `io::Error` returns a string that
// describes the error
Err(why) => panic!("couldn't open {}: {}", display,
why.description()),
Ok(file) => file,
};
println!("file descriptor: {}",file.as_raw_fd());
The closest you can get in the current version of Rust is via native::io::file::open.
use native::io::file::open;
use std::rt::rtio::{Open, Read};
let file = match open(&path.to_c_str(), Open, Read) {
Ok(file) => file,
Err(_) => return,
}
let fd = file.fd();

Resources