Can Rust constant expressions use traits like Default? - rust

This code gives an error:
#[derive(Default)]
struct A {
b: Option<()>,
c: Option<()>,
}
const a: A = A {
b: None,
..Default::default()
};
error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
--> src/lib.rs:9:7
|
9 | ..Default::default()
| ^^^^^^^^^^^^^^^^^^
In this small example it's not a big problem, but if I have a struct composed by multiple structs that implement the Default trait, not being able to use it becomes at minimum an inconvenience.
While I could write this, it wouldn't have the flexibility that Default provides:
impl A {
const fn new(b: Option<()>) -> Self {
A { b, c: None }
}
}
const a: A = A::new(None);
Is there any way to avoid doing that?

The ..Default::default() syntax is not restricted to Default::default(), so you can write a const fn default-like function and use that inside of a constant:
struct A {
b: Option<()>,
c: Option<()>,
}
impl A {
const fn new() -> A {
A {
b: None,
c: None,
}
}
}
impl Default for A {
fn default() -> A {
// implementing using new() instead of #[derive]
// to avoid diverging implementations
A::new()
}
}
const a: A = A {
b: None,
..A::new()
};
Run in Playground

No, it is not possible to use traits in a constant context. This is still being discussed in RFC #2632 — Calling methods on generic parameters of const fns.
See also:
Can I use const with overloading operators in Rust?

Related

How can I make a struct which may or may not have a field defined?

I'm working on an implementation of polynomials which uses a C library in the case of integer coefficients. However, I want to define a different implementation when coefficients come from some other ring. When we will be using the C library we need to handle some underlying values which we pass to C, grouped in a struct. Otherwise, there is no need for these values to be defined. How can I implement this? Here is a mock up of what I want:
pub struct Poly<T> {
coeff_type: T,
c_value: StructDependingOnT, // only needs to be defined when T is an integer for example
}
My thought was to have a trait specifying when a coefficient type means we will be using the C library:
pub struct Poly<T> {
coeff_type: T,
}
pub trait UsesC<T> { // T is the underlying c_value needed above
fn get_c_value(&self) -> T;
}
impl UsesC<StructDependingOnT> for Poly<CoefficientType> {
fn get_c_value(&self) -> StructDependingOnT {
// ??
}
}
The issue here is c_value is not a field of the struct. Is there a way to have a field defined only sometimes, like when it implements a certain trait? Defining an associated constant for UsesC is close to what I want, but it would need to be mutable.
You can't cause the field to disappear, but you can use a zero-sized type.
It requires a bit of trickery using a new trait with an associated type for each T you want to support.
fn main() {
let p1: Poly<f32> = Poly::default();
let p2: Poly<i32> = Poly::default();
println!("p1 = {:?}", p1); // "p1 = Poly { coeff_type: 0.0, c_value: () }"
println!("p2 = {:?}", p2); // "p2 = Poly { coeff_type: 0, c_value: IntRing }"
}
use core::fmt::Debug;
pub trait Polyable {
type Extra: Default + Debug;
}
#[derive(Default, Debug)]
pub struct Poly<T: Polyable> {
coeff_type: T,
c_value: <T as Polyable>::Extra,
}
#[derive(Default, Debug)]
pub struct IntRing {}
impl Polyable for i32 {
type Extra = IntRing;
}
impl Polyable for f32 {
type Extra = ();
}

How can I define AccountId32 constant? [duplicate]

This code gives an error:
#[derive(Default)]
struct A {
b: Option<()>,
c: Option<()>,
}
const a: A = A {
b: None,
..Default::default()
};
error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
--> src/lib.rs:9:7
|
9 | ..Default::default()
| ^^^^^^^^^^^^^^^^^^
In this small example it's not a big problem, but if I have a struct composed by multiple structs that implement the Default trait, not being able to use it becomes at minimum an inconvenience.
While I could write this, it wouldn't have the flexibility that Default provides:
impl A {
const fn new(b: Option<()>) -> Self {
A { b, c: None }
}
}
const a: A = A::new(None);
Is there any way to avoid doing that?
The ..Default::default() syntax is not restricted to Default::default(), so you can write a const fn default-like function and use that inside of a constant:
struct A {
b: Option<()>,
c: Option<()>,
}
impl A {
const fn new() -> A {
A {
b: None,
c: None,
}
}
}
impl Default for A {
fn default() -> A {
// implementing using new() instead of #[derive]
// to avoid diverging implementations
A::new()
}
}
const a: A = A {
b: None,
..A::new()
};
Run in Playground
No, it is not possible to use traits in a constant context. This is still being discussed in RFC #2632 — Calling methods on generic parameters of const fns.
See also:
Can I use const with overloading operators in Rust?

How to use traits for function overloading in Rust? [duplicate]

I am modeling an API where method overloading would be a good fit. My naïve attempt failed:
// fn attempt_1(_x: i32) {}
// fn attempt_1(_x: f32) {}
// Error: duplicate definition of value `attempt_1`
I then added an enum and worked through to:
enum IntOrFloat {
Int(i32),
Float(f32),
}
fn attempt_2(_x: IntOrFloat) {}
fn main() {
let i: i32 = 1;
let f: f32 = 3.0;
// Can't pass the value directly
// attempt_2(i);
// attempt_2(f);
// Error: mismatched types: expected enum `IntOrFloat`
attempt_2(IntOrFloat::Int(i));
attempt_2(IntOrFloat::Float(f));
// Ugly that the caller has to explicitly wrap the parameter
}
Doing some quick searches, I've found some references that talk about overloading, and all of them seem to end in "we aren't going to allow this, but give traits a try". So I tried:
enum IntOrFloat {
Int(i32),
Float(f32),
}
trait IntOrFloatTrait {
fn to_int_or_float(&self) -> IntOrFloat;
}
impl IntOrFloatTrait for i32 {
fn to_int_or_float(&self) -> IntOrFloat {
IntOrFloat::Int(*self)
}
}
impl IntOrFloatTrait for f32 {
fn to_int_or_float(&self) -> IntOrFloat {
IntOrFloat::Float(*self)
}
}
fn attempt_3(_x: &dyn IntOrFloatTrait) {}
fn main() {
let i: i32 = 1;
let f: f32 = 3.0;
attempt_3(&i);
attempt_3(&f);
// Better, but the caller still has to explicitly take the reference
}
Is this the closest I can get to method overloading? Is there a cleaner way?
Yes, there is, and you almost got it already. Traits are the way to go, but you don't need trait objects, use generics:
#[derive(Debug)]
enum IntOrFloat {
Int(i32),
Float(f32),
}
trait IntOrFloatTrait {
fn to_int_or_float(&self) -> IntOrFloat;
}
impl IntOrFloatTrait for i32 {
fn to_int_or_float(&self) -> IntOrFloat {
IntOrFloat::Int(*self)
}
}
impl IntOrFloatTrait for f32 {
fn to_int_or_float(&self) -> IntOrFloat {
IntOrFloat::Float(*self)
}
}
fn attempt_4<T: IntOrFloatTrait>(x: T) {
let v = x.to_int_or_float();
println!("{:?}", v);
}
fn main() {
let i: i32 = 1;
let f: f32 = 3.0;
attempt_4(i);
attempt_4(f);
}
See it working here.
Here's another way that drops the enum. It's an iteration on Vladimir's answer.
trait Tr {
fn go(&self) -> ();
}
impl Tr for i32 {
fn go(&self) {
println!("i32")
}
}
impl Tr for f32 {
fn go(&self) {
println!("f32")
}
}
fn attempt_1<T: Tr>(t: T) {
t.go()
}
fn main() {
attempt_1(1 as i32);
attempt_1(1 as f32);
}
Function Overloading is Possible!!! (well, sorta...)
This Rust Playground example has more a more detailed example, and shows usage of a struct variant, which may be better for documentation on the parameters.
For more serious flexible overloading where you want to have sets of any number of parameters of any sort of type, you can take advantage of the From<T> trait for conversion of a tuple to enum variants, and have a generic function that converts tuples passed into it to the enum type.
So code like this is possible:
fn main() {
let f = Foo { };
f.do_something(3.14); // One f32.
f.do_something((1, 2)); // Two i32's...
f.do_something(("Yay!", 42, 3.14)); // A str, i32, and f64 !!
}
First, define the different sets of parameter combinations as an enum:
// The variants should consist of unambiguous sets of types.
enum FooParam {
Bar(i32, i32),
Baz(f32),
Qux(&'static str, i32, f64),
}
Now, the conversion code; a macro can be written to do the tedious From<T> implementations, but here's what it could produce:
impl From<(i32, i32)> for FooParam {
fn from(p: (i32, i32)) -> Self {
FooParam::Bar(p.0, p.1)
}
}
impl From<f32> for FooParam {
fn from(p: f32) -> Self {
FooParam::Baz(p)
}
}
impl From<(&'static str, i32, f64)> for FooParam {
fn from(p: (&'static str, i32, f64)) -> Self {
FooParam::Qux(p.0, p.1, p.2)
}
}
And then finally, implement the struct with generic method:
struct Foo {}
impl Foo {
fn do_something<T: Into<FooParam>>(&self, t: T) {
use FooParam::*;
let fp = t.into();
match fp {
Bar(a, b) => print!("Bar: {:?}, {:?}\n", a, b),
Baz(a) => print!("Baz: {:?}\n", a),
Qux(a, b, c) => {
print!("Qux: {:?}, {:?}, {:?}\n", a, b, c)
}
}
}
}
Note: The trait bound on T needs to be specified.
Also, the variants need to be composed of combinations of types that the compiler wouldn't find ambiguous - which is an expectation for overloaded methods in other languages as well (Java/C++).
This approach has possibilities... it would be awesome if there's a decorator available - or one were written that did the From<T> implementations automatically when applied to an enum. Something like this:
// THIS DOESN'T EXIST - so don't expect the following to work.
// This is just an example of a macro that could be written to
// help in using the above approach to function overloading.
#[derive(ParameterOverloads)]
enum FooParam {
Bar(i32, i32),
Baz(f32),
Qux(&'static str, i32, f64),
}
// If this were written, it could eliminate the tedious
// implementations of From<...>.
The Builder
Another approach that addresses the case where you have multiple optional parameters to an action or configuration is the builder pattern. The examples below deviate somewhat from the recommendations in the link. Typically, there's a separate builder class/struct which finalizes the configuration and returns the configured object when a final method is invoked.
One of the most relevant situations this can apply to is where you want a constructor that takes a variable number of optional arguments - since Rust doesn't have built-in overloading, we can't have multiple versions of ___::new(). But we can get a similar effect using a chain of methods that return self. Playground link.
fn main() {
// Create.
let mut bb = BattleBot::new("Berzerker".into());
// Configure.
bb.flame_thrower(true)
.locomotion(TractorTreads)
.power_source(Uranium);
println!("{:#?}", bb);
}
Each of the configuration methods has a signature similar to:
fn power_source(&mut self, ps: PowerSource) -> &mut Self {
self.power_source = ps;
self
}
These methods could also be written to consume self and return non-reference copies or clones of self.
This approach can also be applied to actions. For instance, we could have a Command object that can be tuned with chained methods, which then performs the command when .exec() is invoked.
Applying this same idea to an "overloaded" method that we want to take a variable number of parameters, we modify our expectations a bit and have the method take an object that can be configured with the builder pattern.
let mut params = DrawParams::new();
graphics.draw_obj(params.model_path("./planes/X15.m3d")
.skin("./skins/x15.sk")
.location(23.64, 77.43, 88.89)
.rotate_x(25.03)
.effect(MotionBlur));
Alternatively, we could decide on having a GraphicsObject struct that has several config tuning methods, then performs the drawing when .draw() is invoked.

Why must the associated type be specified in a collection of trait object references?

Here is an offending example (Playground):
// Some traits
trait Behaviour {
type Sub: SubBehaviour;
}
trait SubBehaviour {}
// Some implementations of these traits
struct A;
impl Behaviour for A {
type Sub = B;
}
struct B;
impl SubBehaviour for B {}
// Struct that holds a collection of these traits.
struct Example<'a> {
behaviours: Vec<&'a dyn Behaviour>,
}
impl<'a> Example<'a> {
fn add_behaviour<T: Behaviour>(&mut self, b: &'a T) {
self.behaviours.push(b);
}
}
fn main() {
let b = A;
let mut e = Example {
behaviours: Vec::new(),
};
e.add_behaviour(&b);
}
I get:
error[E0191]: the value of the associated type `Sub` (from trait `Behaviour`) must be specified
--> src/main.rs:17:29
|
3 | type Sub: SubBehaviour;
| ----------------------- `Sub` defined here
...
17 | behaviours: Vec<&'a dyn Behaviour>,
| ^^^^^^^^^ help: specify the associated type: `Behaviour<Sub = Type>`
Why must this type must be specified, particularly in this case where we are only storing a reference to the object? How can I get this code to work?
All types must be statically known at compile time. If Rust would allow different associated types for elements of a Vec, type information could depend on indices which are only known at runtime.
I find it helpful to consider a smaller example:
trait Behaviour {
type T;
fn make_t(&self) -> T;
}
fn foo(my_vec: Vec<&dyn Behaviour>, index: usize) {
let t = my_vec[index].make_t(); //Type of t depends on index
}
You were on the right track to fixing this though. I assume you introduced the SubBehaviour trait because you realized you need to put restrictions of what T can be. The thing is, in that case you don't need an associated type anymore.
trait SubBehaviour {}
trait Behaviour {
fn make_t(&self) -> Box<dyn SubBehaviour>;
fn ref_t(&self) -> &dyn SubBehaviour; // also fine
}
fn some_function(my_vec: Vec<&dyn Behaviour>, index: usize) {
let t1 = my_vec[index].make_t();
}
The only limitation is that in your definition of Behaviour you can not do anything which would depend on the size of T, (like allocating it on the stack or moving it) since the size of T can not be specified by the SubBehaviour trait.
You need to specify the associated type of the trait (i.e. Behavior<Sub = ???>).
When adding the associated type at all places, it compiles:
struct Example<'a, S: SubBehaviour + 'a> {
behaviours: Vec<&'a Behaviour<Sub = S>>,
}
impl<'a, S: SubBehaviour> Example<'a, S> {
fn add_behaviour<T: Behaviour<Sub = S>>(&mut self, b: &'a T) {
self.behaviours.push(b);
}
}
See this in action on the Playground
So the answer to your first question is covered by Tim's answer and is correct, you might not want your Example to be generic. In that case, you need to use some sort of type erasure:
// Some traits
trait Behaviour {
type Sub: SubBehaviour;
}
trait SubBehaviour {}
// Some implementations of these traits
struct A;
impl Behaviour for A {
type Sub = B;
}
struct B;
impl SubBehaviour for B {}
struct AnyBehaviour {
closure: Box<Fn()>,
}
impl AnyBehaviour {
fn new<U: SubBehaviour, T: Behaviour<Sub = U>>(b: &T) -> Self {
let closure = || {
//let sub = T::Sub::new();
println!("Can use T here");
};
AnyBehaviour {
closure: Box::new(closure),
}
}
}
// Struct that holds a collection of these traits.
struct Example {
behaviours: Vec<AnyBehaviour>,
}
impl Example {
fn add_behaviour<U: SubBehaviour, T: Behaviour<Sub = U>>(&mut self, b: &T) {
self.behaviours.push(AnyBehaviour::new(b));
}
}
fn main() {
let b = A;
let mut e = Example {
behaviours: Vec::new(),
};
e.add_behaviour(&b);
}
Within the closure, you have access to all the types needed call the traits functions with whatever subtype needed.
Why this happens, is mostly because you actually need a definition of the associated type in order for the trait to be "complete" so the compiler can work with it. Tim's answer answers that by the definition to be higher up in the chain (outside of Example) instead of inside.

How do I create FFI bindings to C functions expecting OR-ed bytes?

I tried to create FFI bindings to libmodbus, written in C.
Here I stumble upon this function
modbus_set_error_recovery(ctx,
MODBUS_ERROR_RECOVERY_LINK |
MODBUS_ERROR_RECOVERY_PROTOCOL);
The second parameter is defined as
typedef enum
{
MODBUS_ERROR_RECOVERY_NONE = 0,
MODBUS_ERROR_RECOVERY_LINK = (1<<1),
MODBUS_ERROR_RECOVERY_PROTOCOL = (1<<2)
} modbus_error_recovery_mode;
My bindgen-generated bindings are these:
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum modbus_error_recovery_mode {
MODBUS_ERROR_RECOVERY_NONE = 0,
MODBUS_ERROR_RECOVERY_LINK = 2,
MODBUS_ERROR_RECOVERY_PROTOCOL = 4,
}
and
extern "C" {
pub fn modbus_set_error_recovery(ctx: *mut modbus_t,
error_recovery:
modbus_error_recovery_mode)
-> ::std::os::raw::c_int;
}
My safe interface looks like this, so far:
pub fn set_error_recovery(&mut self, error_recovery_mode: ErrorRecoveryMode) -> Result<()> {
unsafe {
match ffi::modbus_set_error_recovery(self.ctx, error_recovery_mode.to_c()) {
-1 => bail!(Error::last_os_error()),
0 => Ok(()),
_ => panic!("libmodbus API incompatible response"),
}
}
}
and
use std::ops::BitOr;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ErrorRecoveryMode {
NONE = 0,
Link = 2,
Protocol = 4,
}
impl ErrorRecoveryMode {
pub fn to_c(self) -> ffi::modbus_error_recovery_mode {
match self {
NONE => ffi::modbus_error_recovery_mode::MODBUS_ERROR_RECOVERY_NONE,
Link => ffi::modbus_error_recovery_mode::MODBUS_ERROR_RECOVERY_LINK,
Protocol => ffi::modbus_error_recovery_mode::MODBUS_ERROR_RECOVERY_PROTOCOL,
}
}
}
impl BitOr for ErrorRecoveryMode {
type Output = Self;
fn bitor(self, rhs: ErrorRecoveryMode) -> ErrorRecoveryMode {
self | rhs
}
}
This triggered a stack overflow if I call set_error_recovery like this
assert!(modbus.set_error_recovery(ErrorRecoveryMode::Link | ErrorRecoveryMode::Protocol).is_ok())
The error is
thread 'set_error_recovery' has overflowed its stack
fatal runtime error: stack overflow
As DK. mentioned:
C's enum and Rust's enum have different restrictions.
It's not valid to have a Rust enum that isn't one of the enum variants.
What you have is called "bitflags"
Luckily, Bindgen understands bitflags. If you generate your headers while passing the bitfield-enum flag or by using Builder::bitfield_enum:
bindgen --bitfield-enum modbus_error_recovery_mode fake-modbus.h
Bindgen will generate constants for each C enum value, a newtype wrapper, and implementations of the Bit* traits:
// Many implementation details removed
pub struct modbus_error_recovery_mode(pub ::std::os::raw::c_uint);
pub const modbus_error_recovery_mode_MODBUS_ERROR_RECOVERY_NONE: modbus_error_recovery_mode =
modbus_error_recovery_mode(0);
pub const modbus_error_recovery_mode_MODBUS_ERROR_RECOVERY_LINK: modbus_error_recovery_mode =
modbus_error_recovery_mode(2);
pub const modbus_error_recovery_mode_MODBUS_ERROR_RECOVERY_PROTOCOL: modbus_error_recovery_mode =
modbus_error_recovery_mode(4);
impl ::std::ops::BitOr<modbus_error_recovery_mode> for modbus_error_recovery_mode {}
impl ::std::ops::BitOrAssign for modbus_error_recovery_mode {}
impl ::std::ops::BitAnd<modbus_error_recovery_mode> for modbus_error_recovery_mode {}
impl ::std::ops::BitAndAssign for modbus_error_recovery_mode {}
extern "C" {
pub fn modbus_set_error_recovery(
ctx: *mut modbus_t,
error_recovery: modbus_error_recovery_mode,
) -> ::std::os::raw::c_int;
}
How do I expose the bindgen generated constants to the public
Of course, creating an idiomatic Rust API to non-Rust code is the hard part. I might try something like this:
#[derive(Debug)]
struct Modbus(*mut raw::modbus_t);
#[derive(Debug)]
struct Error;
#[derive(Debug, Copy, Clone)]
enum ErrorRecovery {
Link,
Protocol,
}
impl ErrorRecovery {
fn as_raw(&self) -> raw::modbus_error_recovery_mode {
use ErrorRecovery::*;
match *self {
Link => raw::modbus_error_recovery_mode_MODBUS_ERROR_RECOVERY_LINK,
Protocol => raw::modbus_error_recovery_mode_MODBUS_ERROR_RECOVERY_PROTOCOL,
}
}
}
impl Modbus {
fn set_error_recovery(&mut self, flags: Option<&[ErrorRecovery]>) -> Result<(), Error> {
let flag = flags.unwrap_or(&[]).iter().fold(
raw::modbus_error_recovery_mode_MODBUS_ERROR_RECOVERY_NONE,
|acc, v| acc | v.as_raw(),
);
let res = unsafe { raw::modbus_set_error_recovery(self.0, flag) };
Ok(()) // real error checking
}
}
The problem is that C's enum and Rust's enum are very different things. In particular, C allows an enum to have absolutely any value whatsoever, whether or not that value corresponds to a variant.
Rust does not. Rust relies on enums only ever having a single value of a defined variant, or you run the risk of undefined behaviour.
What you have is not an enumeration (in the Rust sense), you have are bit flags, for which you want the bitflags crate.
As for the stack overflow, that's just because you defined the BitOr implementations in terms of itself; that code is unconditionally recursive.

Resources