trait not satisfied for select(count_star()).first - rust

I'd like to do a count on records of a table named topics. I'm doing it almost by the book
but when I do
let count: u64 = topics.select(count_star()).first(&conn).unwrap();
I keep getting (.first is underlined as error):
[rustc E0277] [E] the trait bound `u64: diesel::deserialize::FromSql<diesel::
sql_types::BigInt, diesel::mysql::Mysql>` is not satisfied
the trait `diesel::deserialize::FromSql<diesel::sql_types::BigInt, diesel::
mysql::Mysql>` is not implemented for `u64`
help: the following implementations were found:
<u64 as diesel::deserialize::FromSql<diesel::sql_types::
// I guess there's more information but Vim's
// Pmenu only shows up to eight lines.
So propably I need to do some type casting here, but honestly, I have no idea at which point.

As the error tells you, count_star generates a BigInt.
And as BigInt's documentation indicates, BigInt is convertible to and from i64, not u64.
I guess despite MySQL having unsigned integers Diesel doesn't support them because SQL doesn't specify unsigned integers, so not all database engines have them (e.g. Postgres does not).
Anyway your count should be an i64, or you need to set up a more explicit conversion from a BigInt to an i64 to an u64.

Related

What does BigUint::from(24u32) do?

BigUint::from(24u32)
I understand that it is related to a big unsigned integer since it is part of a cryptography module to factor big numbers. I don't understand what the part from(24u32) does.
For example, if it is used in this context
let b: BigUint = (BigUint::from(24u32) * &n).sqrt() + &one;
Conversions between types in Rust are defined by the From trait which defines a function from, so that Foo::from (bar) converts the value bar to the type Foo. In your case, you are therefore converting 24u32 to type BigUint, but what's this 24u32?
Integer literals in Rust can use a suffix to specify their actual type. If you wrote just 24, it could be for example an i32, a u8 or a u32. Most of the time the compiler is able to infer the actual type from the way the value is used, and when it can't it defaults to i32. But in your case that won't work: there is no BigUint::from<i32> but there are conversion functions for all the regular unsigned types: u8, u16, u32, u64 and u128, so the compiler doesn't know which one to use. Adding the u32 suffix clarifies the type, so 24u32 is the value 24 with the type u32, allowing the compiler to understand that you want BigUint::from<u32> (24).
The BigUint struct implements the From<u32> trait, which means that it will implement a from(u32) function.
Implementations of the From<_> trait are used to perform a value conversion that will consume the original input. In your example the BigUint struct is constructed from the 24u32 number.

Is it possible to use functions on Rust's constant generics

So say I'm writing a wrapper type for the array.
struct Array<const L: usize, T> {
raw: [T;L]
}
And I have some function that mutates the length of the array wrapper, say the function is concatenation:
impl<const L: usize, T> Array<L, T> {
fn concat<const L2: usize>(self, other: Array<L, T>) -> Array<{L + L2}, T> {todo!();}
}
When I try to compile this code, the rust compiler gets extremely mad. Thinking it might have something to do with adding types corresponding to implementing multiple, traits, i tried multiplication instead of addition, which also didn't work.
I know that rust can evaluate some expressions at compile time, is this just a case where that isn't allowed, or am I missing something?
When I try to compile this code, the rust compiler gets extremely mad. […] I know that rust can evaluate some expressions at compile time, is this just a case where that isn't allowed, or am I missing something?
You say that the compiler gets mad at you, but have you considered listening at what it was telling you?
Plugging your code into the playground, the first error is a trivial showstopper of
error: type parameters must be declared prior to const parameters
--> src/lib.rs:1:30
|
1 | struct Array<const L: usize, T> {
| -----------------^- help: reorder the parameters: lifetimes, then types, then consts: `<T, const L: usize>
so it kind-of doesn't seem to because that's trivial to fix.
Now fixing that we get to the meat of the issue:
= help: const parameters may only be used as standalone arguments, i.e. `L`
= help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions
that seems to explain the entirety of the thing: what's currently enabled in Rust is the
Const generics MVP
MVP as in minimum viable products aka the smallest featureset which can be useful, and what that translates to is explained in the introductory blog post.
The first limitation is
Only integral types are permitted for const generics
which is fine here because you're using integral types, but the second limitation is
No complex generic expressions in const arguments
What is a complex generic expression? Anything outside of:
A standalone const parameter.
A literal (i.e. an integer, bool, or character).
A concrete constant expression (enclosed by {}), involving no generic parameters.
What you're trying to do does not work (outside of nightly with both const_generics and const_evaluatable_checked enabled) because you're writing constant expressions involving at least one generic parameter, which is not part of the MVP.

Creating Diesel.rs queries with a dynamic number of .and()'s

While playing with Diesel, I got stuck writing a function which takes an vector of Strings as input and does the following:
Combine all Strings to a large query
run the query on the Connection
process the result
return a Vec
If I construct the query in one step, it works just fine:
fn get_books(authors: Vec<String>, connection: SqliteConnection) {
use schema::ebook::dsl::*;
let inner = author
.like(format!("%{}%", authors[0]))
.and(author.like(format!("%{}%", authors[1])))
.and(author.like(format!("%{}%", authors[2])));
ebook
.filter(inner)
.load::<Ebook>(&connection)
.expect("Error loading ebook");
}
If I try to generate the query in more steps (needed in order to work with the variable length of the input vector) I can't get it to compile:
fn get_books(authors: Vec<String>, connection: SqliteConnection) {
use schema::ebook::dsl::*;
let mut inner = author
.like(format!("%{}%", authors[0]))
.and(author.like(format!("%{}%", authors[1]))); // <1>
inner = inner.and(author.like(format!("%{}%", authors[2]))); // <2>
ebook
.filter(inner)
.load::<Ebook>(&connection)
.expect("Error loading ebook");
}
This generates the following error:
inner = inner.and(author.like(format!("%{}%",authors[2])));
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `diesel::expression::operators::Like`, found struct `diesel::expression::operators::And`
I don't understand why Rust expects a Like and not an And. The function an the line line marked <1> returns an And and therefore an And is stored in inner.
What am I missing? Why does the first bit of code compile and the second won't? What is the right way to generate this kind of query?
The first thing you need to do is look at the complete error message:
error[E0308]: mismatched types
--> src/main.rs:23:13
|
23 | inner = inner.and(author.like(format!("%{}%", authors[2])));//<2>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `diesel::expression::operators::Like`, found struct `diesel::expression::operators::And`
|
= note: expected type `diesel::expression::operators::And<diesel::expression::operators::Like<_, _>, _>`
found type `diesel::expression::operators::And<diesel::expression::operators::And<diesel::expression::operators::Like<_, _>, diesel::expression::operators::Like<schema::ebook::columns::author, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>>, _>`
It's long, but that's because it's fully qualified. Lets shorten the last part a bit:
expected type `And<Like<_, _>, _>`
found type `And<And<Like<_, _>, Like<author, Bound<Text, String>>>, _>`
If you review the documentation for and, you'll see that every call to and consumes the receiver and returns a brand new type — And:
fn and<T: AsExpression<Bool>>(self, other: T) -> And<Self, T::Expression>
This is the core of Diesel's ability to generate strongly-typed SQL expressions with no run-time overhead. All of the work is delegated to the type system. In fact, the creator of Diesel has an entire talk where he shows how far Diesel pushes the type system and what benefits it has.
Turning back to your question, it's impossible to store an And<_, _> in the same location as an And<And<_, _>, _> because they will have different sizes and are in fact different types. At the root, this is the same as trying to store an integer in a boolean.
In fact, there's no way to know what concrete type you need because you don't even know how many conditions you will have — it depends on the size of the vector.
In this case, we have to give up static dispatch and move to dynamic dispatch via a trait object. Diesel has a specific trait for this case (which also has good examples): BoxableExpression.
The remaining piece is to convert your authors to like expressions and combine them. We need a base case, however, for when authors is empty. We construct a trivially true statement (author = author) for that.
#[macro_use]
extern crate diesel;
use diesel::SqliteConnection;
use diesel::prelude::*;
use diesel::sql_types::Bool;
mod schema {
table! {
ebook (id) {
id -> Int4,
author -> Text,
}
}
}
fn get_books(authors: Vec<String>, connection: SqliteConnection) {
use schema::ebook::dsl::*;
let always_true = Box::new(author.eq(author));
let query: Box<BoxableExpression<schema::ebook::table, _, SqlType = Bool>> = authors
.into_iter()
.map(|name| author.like(format!("%{}%", name)))
.fold(always_true, |query, item| {
Box::new(query.and(item))
});
ebook
.filter(query)
.load::<(i32, String)>(&connection)
.expect("Error loading ebook");
}
fn main() {}
I also wouldn't be surprised if there wasn't a better SQL way of doing this. It appears that PostgreSQL has the WHERE col LIKE ANY( subselect ) and WHERE col LIKE ALL( subselect ) forms, for example.

Why is type conversion from u64 to usize allowed using `as` but not `From`?

The first conversion using 'as' compiles, but the second one using the 'From' trait does not:
fn main() {
let a: u64 = 5;
let b = a as usize;
let b = usize::from(a);
}
Using Rust 1.34.0, I get the following error:
error[E0277]: the trait bound `usize: std::convert::From<u64>` is not satisfied
--> src/main.rs:4:13
|
4 | let b = usize::from(a);
| ^^^^^^^^^^^ the trait `std::convert::From<u64>` is not implemented for `usize`
|
= help: the following implementations were found:
<usize as std::convert::From<bool>>
<usize as std::convert::From<std::num::NonZeroUsize>>
<usize as std::convert::From<u16>>
<usize as std::convert::From<u8>>
= note: required by `std::convert::From::from`
When I replace u64 with u8, there is no more error. From the error message, I understand that the From trait is implemented only for u8, but not for the other integer types.
If there is a good reason for that, then why shouldn't the conversion using 'as' should also fail to compile?
as casts are fundamentally different from From conversions. From conversions are "simple and safe" whereas as casts are purely "safe". When considering numeric types, From conversions exist only when the output is guaranteed to be the same, i.e. there is no loss of information (no truncation or flooring or loss of precision). as casts, however, do not have this limitation.
Quoting the docs,
The size of [usize] is "how many bytes it takes to reference any location in memory. For example, on a 32 bit target, this is 4 bytes and on a 64 bit target, this is 8 bytes."
Since the size depends on the target architecture and cannot be determined before compilation, there is no guarantee that a From conversion between a numeric type and usize is possible. An as cast, however, will always operate by the rules listed here.
For instance, on a 32-bit system, usize is equivalent to u32. Since a usize is smaller than a u64, there can be loss of information (truncation) when converting a u64 into a usize and hence a From conversion cannot exist. However, the size of a usize is always guaranteed to be 8 bits or greater and a u8 to usize From conversion will always exist.
As already mentioned, converting from a 64-bit value to a usize might cause truncation; you might lose data when a usize is 16 or 32 bits.
Fallable conversions are covered by the TryFrom trait, available in Rust 1.34:
use std::convert::TryFrom;
fn main() {
let a: u64 = 5;
let b = a as usize;
let b = usize::try_from(a);
}
See also:
How do I convert a usize to a u32 using TryFrom?

Make String type compatible with ARM?

I need to compile a program for an ARM device. It seems to be failing here, perhaps because of the type difference on ARM?
unsafe { Ok(String::from(try!(CStr::from_ptr(buf.as_ptr() as *const i8).to_str()))) }
The error is:
694 | unsafe { Ok(String::from(try!(CStr::from_ptr(buf.as_ptr() as *const i8).to_str()))) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected u8, found i8
|
= note: expected type `*const u8`
found type `*const i8`
What is the type difference and how do I fix it?
You probably want to use std::os::raw::c_char instead of i8. (Though that may not be the right place to get the type from. libc::c_char seems to exist as well.)
The basic issue is that the char type in C can be signed or unsigned depending on the platform and that is reflected in the foreign function interface. Ideally you'd like to find a way to do the conversion without having to explicitly mention the type.

Resources