I could not figure out how to iterate over a collection and execute statements one by one with Tiberius.
My current code looks like this (simplified):
use futures::Future;
use futures_state_stream::StateStream;
use tokio::executor::current_thread;
use tiberius::SqlConnection;
fn find_files(files: &mut Vec<String>) {
files.push(String::from("file1.txt"));
files.push(String::from("file2.txt"));
files.push(String::from("file3.txt"));
}
fn main() {
let mut files: Vec<String> = Vec::new();
find_files(&mut files);
let future = SqlConnection::connect(CONN_STR)
.and_then(|conn| {
conn.simple_exec("CREATE TABLE db.dbo.[Filenames] ( [Spalte 0] varchar(80) );")
})
.and_then(|(_, conn)| {
for k in files.iter() {
let sql = format!("INSERT INTO db.dbo.Filenames ([Spalte 0]) VALUES ('{}')", k);
&conn.simple_exec(sql);
}
Ok(())
});
current_thread::block_on_all(future).unwrap();
}
I got the following error message
error[E0382]: use of moved value: `conn`
--> src/main.rs:23:18
|
20 | .and_then(|(_, conn)| {
| ---- move occurs because `conn` has type `tiberius::SqlConnection<std::boxed::Box<dyn tiberius::BoxableIo>>`, which does not implement the `Copy` trait
...
23 | &conn.simple_exec(sql);
| ^^^^ value moved here, in previous iteration of loop
I'm new to Rust but I know there is something wrong with the use of the conn variable but nothing works.
There are actual two questions here:
The header question: how to perform multiple sequential statements using tiberius?
The specific question concerning why an error message comes from a specific bit of code.
I will answer them separately.
Multiple statements
There are many ways to skin a cat. In TDS (the underlying protocol Tiberius is implementing) there is the possibility to execute several statements in a single command. They just need to be delimited by using semicolon. The response from such an execution is in Tiberius represented a stream of futures, one for each statement.
So if your chain of statements is not too big to fit into one command, just build one string and send it over:
fn main() {
let mut files: Vec<String> = Vec::new();
find_files(&mut files);
let stmts = vec![
String::from(
"CREATE TABLE db.dbo.[Filenames] ( [Spalte 0] varchar(80) )")]
.into_iter()
.chain(files.iter().map(|k|
format!("INSERT INTO db.dbo.Filenames ([Spalte 0]) VALUES ('{}')", k)))
.collect::<Vec<_>>()
.join(";");
let future
= SqlConnection::connect(std::env::var("CONN_STR").unwrap().as_str())
.and_then(|conn|
conn.simple_exec(stmts)
.into_stream()
.and_then(|future| future)
.for_each(|_| Ok(())));
current_thread::block_on_all(future).unwrap();
}
There is some simple boilerplate in that example.
simple_exec returns an ExecResult, a wrapper around the individual statement's future results. Calling `into_stream() on that provides a stream of those futures.
That stream of results need to be forced to be carried out, one way of doing that is to call and_then, which awaits each future and does something with it.
We don't actually care about the results here, so we just do a noop for_each.
But, say that there is a lot of statements, more than can fit in a single TDS command, then there is a need to issue them separately (another case is when the statements themselves depend on earlier ones). A version of that problem is solved in How do I iterate over a Vec of functions returning Futures in Rust? .
Then finally, what is your specific error? Well conn is consumed by simple_exec, so it cannot be used afterwards, that is what the error tells you. If you want to use the connection after that execution is done you have to use the Future it returns, which is wrapping the mutated connection. I defer to the link above, on one way to do that.
Related
I noticed that Rust does not have exceptions. How to do error handling in Rust and what are the common pitfalls? Are there ways to control flow with raise, catch, reraise and other stuff? I found inconsistent information on this.
Rust generally solves errors in two ways:
Unrecoverable errors. Once you panic!, that's it. Your program or thread aborts because it encounters something it can't solve and its invariants have been violated. E.g. if you find invalid sequences in what should be a UTF-8 string.
Recoverable errors. Also called failures in some documentation. Instead of panicking, you emit a Option<T> or Result<T, E>. In these cases, you have a choice between a valid value Some(T)/Ok(T) respectively or an invalid value None/Error(E). Generally None serves as a null replacement, showing that the value is missing.
Now comes the hard part. Application.
Unwrap
Sometimes dealing with an Option is a pain in the neck, and you are almost guaranteed to get a value and not an error.
In those cases it's perfectly fine to use unwrap. unwrap turns Some(e) and Ok(e) into e, otherwise it panics. Unwrap is a tool to turn your recoverable errors into unrecoverable.
if x.is_some() {
y = x.unwrap(); // perfectly safe, you just checked x is Some
}
Inside the if-block it's perfectly fine to unwrap since it should never panic because we've already checked that it is Some with x.is_some().
If you're writing a library, using unwrap is discouraged because when it panics the user cannot handle the error. Additionally, a future update may change the invariant. Imagine if the example above had if x.is_some() || always_return_true(). The invariant would changed, and unwrap could panic.
? operator / try! macro
What's the ? operator or the try! macro? A short explanation is that it either returns the value inside an Ok() or prematurely returns error.
Here is a simplified definition of what the operator or macro expand to:
macro_rules! try {
($e:expr) => (match $e {
Ok(val) => val,
Err(err) => return Err(err),
});
}
If you use it like this:
let x = File::create("my_file.txt")?;
let x = try!(File::create("my_file.txt"));
It will convert it into this:
let x = match File::create("my_file.txt") {
Ok(val) => val,
Err(err) => return Err(err),
};
The downside is that your functions now return Result.
Combinators
Option and Result have some convenience methods that allow chaining and dealing with errors in an understandable manner. Methods like and, and_then, or, or_else, ok_or, map_err, etc.
For example, you could have a default value in case your value is botched.
let x: Option<i32> = None;
let guaranteed_value = x.or(Some(3)); //it's Some(3)
Or if you want to turn your Option into a Result.
let x = Some("foo");
assert_eq!(x.ok_or("No value found"), Ok("foo"));
let x: Option<&str> = None;
assert_eq!(x.ok_or("No value found"), Err("No value found"));
This is just a brief skim of things you can do. For more explanation, check out:
http://blog.burntsushi.net/rust-error-handling/
https://doc.rust-lang.org/book/ch09-00-error-handling.html
http://lucumr.pocoo.org/2014/10/16/on-error-handling/
If you need to terminate some independent execution unit (a web request, a video frame processing, a GUI event, a source file to compile) but not all your application in completeness, there is a function std::panic::catch_unwind that invokes a closure, capturing the cause of an unwinding panic if one occurs.
let result = panic::catch_unwind(|| {
panic!("oh no!");
});
assert!(result.is_err());
I would not grant this closure write access to any variables that could outlive it, or any other otherwise global state.
The documentation also says the function also may not be able to catch some kinds of panic.
I've been following tutorials, accepted answers, and the docs. They never worked for me since the beginning of learning, and now I'm stuck at it again.
Imports:
use std::io::prelude::*;
use std::fs::{File, OpenOptions};
use std::io::Read;
use std::io::BufReader;
use std::io::BufRead;
use std::io::Write;
Code:
let mut file_help = OpenOptions::new().append(true).create_new(true).open("n.txt").expect(".");
let mut file_help = BufReader::new(file_help);
Loop for vec:
for i in d_call {file_help.write(format!("{}\n",i))};
In-loop variants that are giving out same errormethod not found in `std::io::BufReader<std::fs::File>:
file_help.write_all(format!("{}\n",i))
write!(file_help, "{}\n",i)
file_help.write(format!("{}\n",i.to_string()))
writeln!(file_help, "{}", i.to_string())
error[E0599]: no method named `write` found for struct `BufReader` in the current scope
--> src/main.rs:21:19
|
21 | file_help.write(format!("{}\n", i.to_string()))
| ^^^^^ method not found in `BufReader<File>`
Playground.
The issue is actually quite simple. You are trying to write data to a BufReader. It can buffer file reads, but does not implement any write functionality. You likely want to use a std::io::BufWriter instead.
Is it possible to write a function that calls BufRead::fill_buf until io::ErrorKind::Interrupted no longer occurs without using unsafe?
With unsafe code, it is:
use std::{
io::{self, BufRead},
slice,
};
fn fill_buf_and_ignore_interrupts(reader: &mut impl BufRead) -> io::Result<&[u8]> {
let (buf_ptr, buf_len) = loop {
match reader.fill_buf() {
Ok(buf) => (buf.as_ptr(), buf.len()),
Err(e) => {
if e.kind() != io::ErrorKind::Interrupted {
return Err(e);
}
}
}
};
Ok(unsafe { slice::from_raw_parts(buf_ptr, buf_len) })
}
If I try to return or break Ok(buf), I get an error from the borrow checker:
error[E0499]: cannot borrow `*reader` as mutable more than once at a time
--> src/lib.rs:8:15
|
6 | fn fill_buf_and_ignore_interrupts(reader: &mut impl BufRead) -> io::Result<&[u8]> {
| - let's call the lifetime of this reference `'1`
7 | let (buf_ptr, buf_len) = loop {
8 | match reader.fill_buf() {
| ^^^^^^ mutable borrow starts here in previous iteration of loop
9 | Ok(buf) => return Ok(buf),
| ------- returning this value requires that `*reader` is borrowed for `'1`
I have tried to replace the loop with recursion, but the error stays. I have also tried to use #![feature(nll)], but it does not work either.
You cannot, and neither should you want to for reasons highlighted below (they have nothing to do with the reference return or unsafe). fill_buf does not work the way you think it does, due to one important caveat from the doc:
Returns the contents of the internal buffer, filling it with more data
from the inner reader if it is empty.
In other words, a subsequent call to fill_buf without consume will be a no-op if the trait is implemented properly and follows the contract stated in the doc. As such, doing one without the other is fraught with risk if you forget that requirement anywhere in anything that calls fill_buf.
Two solutions:
If you're reading to the end of a stream (which is what a BufReader does in quite a few other languages), just BufRead::read_to_end(), BufRead::read_line() or BufRead::read_until(). If you can identify a delimiter, read_until can be easily be worked into an iterable structure
If you are trying to peek at the data and possibly wait for more, you'll need to implement your own trait. This is not a frequent need as most people trying to peek into a stream are looking for a Pattern or a single byte, which BufRead covers.
As a quick summary, fill_buf does not do what you think it does, and you should not need to call it more than once without consuming the internal state of the buffer. If you are not trying to do this, then the lower-level methods of BufRead are not the tool for the job.
I don't think it's possible in safe Rust today for the loop to return a reference to fill_buf's return. (At least I hit my head against this for a while before giving up.)
But...you could just call fill_buf an extra time. If the buffer is already filled, the call should return Ok, and I'd expect the cost to be minimal. (Maybe inlining and optimization will eliminate it entirely.) If that's not true, the underlying BufRead implementation is bad.
I just did something similar in a BufRead adapter that skips escape bytes (looping over the underlying stream's fill_buf when the current chunk has no more non-escape bytes to pass along).
use std::io::{self, BufRead};
pub fn fill_buf_and_ignore_interrupts(reader: &mut impl BufRead) -> io::Result<&[u8]> {
while let Err(e) = reader.fill_buf() {
if e.kind() != io::ErrorKind::Interrupted {
return Err(e);
}
}
reader.fill_buf()
}
Pay no attention to the other answer that says you must never call fill_buf without consume. It's wrong. You should simply expect that if you call fill_buf again without calling consume first, the stream will be at the same position. You only need to call consume when you want the stream to advance. (And I don't know how you could end up with the nonsense idea that you should call consume after fill_buf returns error...)
I have found the answer here: Rust lifetime issue in loop
Rustc can't "deal" with conditional borrowing returns
So, for now, unsafe in such case is the only answer.
Consider the following array -of strings-:
let arrayStrings = ["H", "e", "l", "l", "o"]
For combining its elements (to get "Hello" as single String), we could:
reduce it:
let reducedString = arrayStrings.reduce("", { $0 + $1 }) // "Hello"
Or join it:
let joinedString = arrayStrings.joined() // "Hello"
Both would return "Hello" String as output.
However, what is the logic to keep in mind to determine what is the better choice for such a process? What is the difference when comparing based on the performance?
There are two reasons why joined is a better choice than reduce:
Readability
If you want to join multiple strings into one string, why would you use reduce, with manual concatenation? If there is a specific function for the task you want to do, use it. When reading the code, it's easier to understand joined than reduce.
Performance
joined for String can be implemented better than reduce. It does not have to be but it can. reduce operates on one element at a time, without knowledge about the other elements, with many temporary variables passed around. joined has the knowledge of the entire sequence and it knows that the operation is always the same, therefore it can optimize. It can even use the internal structure of String. See String.joined implementation.
In summary, always use the more specific implementation.
Note that the performance reason above is the less important one.
Update The previous results were obtained by running an iOS app on the simulator. Running the app on a real device, or running the code from a MacOS command line app gives similar results to ones #Sulthan mentioned.
Interestingly enough, reduce gave better results on my machine:
func benchmark(_ label: String, times: Int = 100000, _ f: () -> Void) {
let start = CACurrentMediaTime()
(0..<times).forEach { _ in f() }
let end = CACurrentMediaTime()
print("\(label) took \(end-start)")
}
let arrayStrings = ["H", "e", "l", "l", "o"]
benchmark("reduce", { _ = arrayStrings.reduce("", +) } )
benchmark("join", { _ = arrayStrings.joined() })
The results were around the following numbers when run from the main method of a typical iOS app, build in Debug mode:
reduce took 0.358474982960615
join took 0.582276367989834
Same app, built in Release mode, gave the same order of results:
reduce took 0.126910287013743
join took 0.0291724550188519
I ran the benchmarks multiple times, and reduce performed better in all cases. The difference is not that big though, so unless your string operations are critical in regards to performance, I'd recommend using joined, that method carries more semantical value, it better transmits the intent.
I am using the following code to filter a large array:
var arrayOfSelectedRowDetails = self.projectRowDetails.filter(
{ $0.projectNumber == self.projectNumberArray[selectedRow] }
)
Normally the code runs fine and I have no issues. But in one scenario (after I have deleted some management objects from the persistent store) and then rerun the code I am getting a EXC_BAD_ACCESS (code = 1, address=0x0) error at runtime.
I have set a break and stepped through the runtime of this statement. It is a large array built from a core data entity (using a fetch statement) - and therefore takes a long time. When I step through the code over the first dozen or so indexes the code runs ok - when i remove the break and let it run it then presents the error.
Is it possible to println() from within the closure statement to assist with debugging? I have tried a number of different syntaxes and cannot get it to work.
Alternatively, is it possible to set an error capture statement within the closure so that the code ceases through a break or an abort() statement?
Fundamentally i am trying to identify the index of the array at the point that the error occurs so that I can get sufficient information to debug the delete function (which is where I think the error is). I do not seem to be able to ascertain the index from the info available to me when the error occurs.
This is the first time I have tried programming in Swift and making use of closures so I am learning as I go. Apologies if I am asking fundamental questions. I have not been able to find a similar question elsewhere here with an answer that works.
You can set an exception breakpoint in Xcode (for an example see here).
Also, I suggest that you move the access to self.projectNumberArray out of the closure:
let pn = self.projectNumberArray[selectedRow]
var arrayOfSelectedRowDetails = self.projectRowDetails.filter(
{ $0.projectNumber == pn }
)
The change might not solve the issue, but it will at least help the debugging.
Lastly, if you want to print the index, the following approach will probably work:
let pn = self.projectNumberArray[selectedRow]
var index = 0
var arrayOfSelectedRowDetails = self.projectRowDetails.filter(
{ println(index++); return $0.projectNumber == pn }
)