Is there a way to apply Rust's feature flag support to function parameters? I have a function which takes bunch of inputs, but one of the inputs should only be passed if a feature is active. Of course I can make that parameter optional like:
pub fn my_function(input1: usize, input2: String, input3: Option<Vec<u32>>) -> () {...}
But is there a way to instead make use of the feature flag and define something like this?:
pub fn my_function(
input1: usize,
input2: String,
#[cfg(feature = "my-feature")]
input3: Vec<u32>
) -> () {...}
Similarly, later calling it as: my_function(0, "", #[cfg(feature = "my-feature")] vec![0])?
Rust's feature flags are a thin wrapper around conditional compilation. Imagine how you'd write your code if the feature were always enabled, then again if it were always disabled, and wrap the lines that differ between the two with #[cfg(..]) expressions. To define a function that differs, you can define two different versions and have them call a shared inner block if they're nearly the same, or just define them separately if they should do different things:
struct Thing;
impl Thing {
#[cfg(not(feature = "my-feature"))]
pub fn my_function(input1: usize, input2: String) {
Thing::my_function_inner(input1, input2, None);
}
#[cfg(feature = "my-feature")]
pub fn my_function(input1: usize, input2: String, input3: Vec<u32>) {
Thing::my_function_inner(input1, input2, Some(input3));
}
#[inline]
fn my_function_inner(input1: usize, input2: String, input3: Option<Vec<u32>>) {
todo!();
}
}
Alternatively, you can simply tag the conditional argument with a cfg expression and use conditional compilation within the function body:
impl Thing {
pub fn my_function(
input1: usize,
input2: String,
#[cfg(feature = "my-feature")] input3: Vec<u32>,
) {
// common implementation
#[cfg(feature = "my-feature")]
{ /* `input3` is only available when feature is enabled */ }
#[cfg(not(feature = "my-feature"))]
{ /* only run when feature is disabled */ }
}
}
Choose whichever makes the most sense for your use case. The compiler will likely optimize them similarly.
To call this, you can either rely on the feature flag always being enabled/disabled and just use the corresponding version, or you can include code for both like so:
fn main() {
#[cfg(not(feature = "my-feature"))]
Thing::my_function(0, "".to_string());
#[cfg(feature = "my-feature")]
Thing::my_function(0, "".to_string(), vec![0]);
}
EDIT: To support conditionally compiled return types, you can declare your return type to be a type alias that is itself conditionally compiled, like so:
#[cfg(feature = "my-feature")]
type ReturnType = (usize, usize);
#[cfg(not(feature = "my-feature"))]
type ReturnType = (usize,);
fn my_function() -> ReturnType { todo!() }
Thanks to #ChayimFriedman and #Jmb.
Related
Consider the following type alias and function definitions:
#[cfg(feature = "my-feature")]
pub type CompositeOutputType = (usize, String);
#[cfg(not(feature = "my-feature"))]
pub type CompositeOutputType = usize;
pub fn my_function(input: usize) -> CompositeOutputType {
#[cfg(feature = "my-feature")]
(input, ""); // Why it throws error without this semicolon?
#[cfg(not(feature = "my-feature"))]
input
}
fn main() {
#[cfg(feature = "my-feature")]
let (output_1, output_2) = my_function(1);
#[cfg(not(feature = "my-feature"))]
let output = my_function(1);
println!("output: {:?}", output);
}
Is there a better way to write this in Rust, where the function output depends on the feature? Note that I can also defined CompositeOutputType as a struct instead, so then I do not need to worry about output being a single element or tuple, as it always will be a single object. Hence, my main function might simplify, but I was wondering if there is a better way to write this, to simplify outputting of my_function.
Hence, my main function might simplify, but I was wondering if there is a better way to write this, to simplify outputting of my_function.
Modules?
#[cfg(feature = "my-feature")]
mod inner {
pub type CompositeOutputType = (usize, String);
pub fn my_function(input: usize) -> CompositeOutputType {
(input, "".to_string())
}
}
#[cfg(not(feature = "my-feature"))]
mod inner {
pub type CompositeOutputType = usize;
pub fn my_function(input: usize) -> CompositeOutputType {
input
}
}
pub use inner::*;
though the entire thing seems misdesigned to me, changing the interface of a function entirely depending on a feature seems extremely wonky and error-prone, even with compiler support.
If a feature requires changing code, you might as well expose different types and functions entirely.
I'm working with two crates: A and B. I control both. I'd like to create a struct in A that has a field whose type is known only to B (i.e., A is independent of B, but B is dependent on A).
crate_a:
#[derive(Clone)]
pub struct Thing {
pub foo: i32,
pub bar: *const i32,
}
impl Thing {
fn new(x: i32) -> Self {
Thing { foo: x, bar: &0 }
}
}
crate_b:
struct Value {};
fn func1() {
let mut x = A::Thing::new(1);
let y = Value {};
x.bar = &y as *const Value as *const i32;
...
}
fn func2() {
...
let y = unsafe { &*(x.bar as *const Value) };
...
}
This works, but it doesn't feel very "rusty". Is there a cleaner way to do this? I thought about using a trait object, but ran into issues with Clone.
Note: My reason for splitting these out is that the dependencies in B make compilation very slow. Value above is actually from llvm_sys. I'd rather not leak that into A, which has no other dependency on llvm.
The standard way to implement something like this is with generics, which are kind of like type variables: they can be "assigned" a particular type, possibly within some constraints. This is how the standard library can provide types like Vec that work with types that you declare in your crate.
Basically, generics allow Thing to be defined in terms of "some unknown type that will become known later when this type is actually used."
Given the example in your code, it looks like Thing's bar field may or may not be set, which suggests that the built-in Option enum should be used. All you have to do is put a type parameter on Thing and pass that through to Option, like so:
pub mod A {
#[derive(Clone)]
pub struct Thing<T> {
pub foo: i32,
pub bar: Option<T>,
}
impl<T> Thing<T> {
pub fn new(x: i32) -> Self {
Thing { foo: x, bar: None }
}
}
}
pub mod B {
use crate::A;
struct Value;
fn func1() {
let mut x = A::Thing::new(1);
let y = Value;
x.bar = Some(y);
// ...
}
fn func2(x: &A::Thing<Value>) {
// ...
let y: &Value = x.bar.as_ref().unwrap();
// ...
}
}
(Playground)
Here, the x in B::func1() has the type Thing<Value>. You can see with this syntax how Value is substituted for T, which makes the bar field Option<Value>.
If Thing's bar isn't actually supposed to be optional, just write pub bar: T instead, and accept a T in Thing::new() to initialize it:
pub mod A {
#[derive(Clone)]
pub struct Thing<T> {
pub foo: i32,
pub bar: T,
}
impl<T> Thing<T> {
pub fn new(x: i32, y: T) -> Self {
Thing { foo: x, bar: y }
}
}
}
pub mod B {
use crate::A;
struct Value;
fn func1() {
let mut x = A::Thing::new(1, Value);
// ...
}
fn func2(x: &A::Thing<Value>) {
// ...
let y: &Value = &x.bar;
// ...
}
}
(Playground)
Note that the definition of Thing in both of these cases doesn't actually require that T implement Clone; however, Thing<T> will only implement Clone if T also does. #[derive(Clone)] will generate an implementation like:
impl<T> Clone for Thing<T> where T: Clone { /* ... */ }
This can allow your type to be more flexible -- it can now be used in contexts that don't require T to implement Clone, while also being cloneable when T does implement Clone. You get the best of both worlds this way.
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.
I have the following:
pub struct OpBStruct {
title: String,
output_vale: i32,
}
impl OpBStruct {
pub fn new_OpB(in_title: String, in_output_vale: i32) -> OpBStruct {
OpBStruct {
title: in_title,
output_vale: in_output_vale,
}
}
}
pub struct OpCStruct {
title: String,
another_value: String,
output_vale: i32,
}
impl OpCStruct {
pub fn new_OpC(in_title: String, in_another_value: String, in_output_vale: i32) -> OpCStruct {
OpCStruct {
title: in_title,
another_value: in_another_value,
output_vale: in_output_vale,
}
}
}
impl A {
pub fn new_A(in_name: String, in_operator: Op) -> A {
A {
name: in_name,
operator: in_operator,
}
}
}
pub enum Op {
OpB(OpBStruct),
OpC(OpCStruct),
}
pub struct A {
name: String,
operator: Op,
}
impl A {
pub fn new_A(in_name: String, in_operator: Op) -> A {
A {
name: in_name,
operator: in_operator,
}
}
}
The exact structure of OpBStruct and OpCStruct are arbitrary and could be anything.
How do I make sure OpBStruct and OpCStruct implement a certain trait?
trait OpTrait {
pub fn get_op_output(&self) -> i32;
}
I thought about making a sort of constructor function that checked for an OpTrait trait requirement and it would be the only way one could create an Op instance, but each operator requires different initialization parameters and there's no way to specify a variable number of inputs for a function in Rust.
Something like this doesn't work because there's no way to input the initialization parameters:
pub fn new_op<T: OpTrait>(operator: T) {
// --snip--
}
I thought about somehow using the new_A method implemented on A to check if the in_operator has implemented the trait, but I'm not sure how to do that either.
What is the correct pattern for this? If there is none, I can just implement the trait for each Op with no sort of interface around it.
I would also recommend writing a test, however you can write a function which is generic over a type but takes no arguments:
struct X {}
trait Y {
fn yo();
}
fn is_y<T: Y>(){}
Then you can add the following line to do the check
is_y::<X>();
which will compile only if X implements Y.
Using a unit test would be a fairly straightforward way to enforce that you want a given trait on a struct. You can do it via implicit test code, but a small utility function to do so can express the intent a little more clearly.
If you have indicated trait inputs on functions in the rest of the code, it might come out fairly naturally without the unit test. The test has the advantage of letting you have an opportunity to explicitly check some invariant of the trait implementation too.
struct A {
val: u8,
}
struct B {
val: u32,
}
trait ExpandToU64 {
fn to_u64(&self) -> u64;
}
impl ExpandToU64 for A {
fn to_u64(&self) -> u64
{
self.val as u64
}
}
fn trait_tester<E>(a: E)
where E: ExpandToU64
{
// the utility function doesn't have to even use the trait...
// but you probably want to exercise the logic a bit
//let v = a.to_u64();
let v = 24u64;
println!("{:?}", v);
}
#[test]
fn test_needs_trait_ExpandToU64() {
let a = A { val:1 };
trait_tester(a);
let b = B { val:2 };
trait_tester(b);
// This fails with a compile error
// "the trait `ExpandToU64` is not implemented for `B`"
}
During build.rs time, I'm generating Rust code that calls a C function with a variable number of arguments:
//build.rs
fn generate_code(param_name: &str, param_type: &str);
This generates:
c_func(x, {param_name});
where param_type is any integer type.
The problem is with "integer promotions". For example, this code gives a compile-time error because p should be promoted to a c_uint by the C calling conventions for variadics and Rust does not do it implicitly:
use std::os::raw::c_uint;
extern "C" {
fn c_func(x: c_uint, ...);
}
fn main() {
let x: c_uint = 1;
let p: u8 = 2;
c_func(x, p);
}
To promote the integer, I need to know the size of c_uint on the target platform to convert integer types with size <= c_uint to c_uint (and signed types to c_int),
Maybe there is some kind of trick I can do with the help of generated code (
like c_func(x, p.promote());), where the promote function is part of some magic trait that promotes to the suitable type?
I do not believe there's any way of accessing that information in the build script.
where the promote function is part of some magic trait that promotes to the suitable type?
That sounds like a workable solution. You can implement this trait for various types depending on the target platform:
extern crate libc;
trait Promote {
type Output;
fn promote(self) -> Self::Output;
}
// This can be made into a macro for all the "easy" cases
impl Promote for u8 {
type Output = libc::c_uint;
fn promote(self) -> Self::Output {
self as Self::Output
}
}
#[cfg(target_pointer_width = "32")]
impl Promote for u64 {
type Output = libc::uint64_t;
fn promote(self) -> Self::Output {
self as Self::Output
}
}
#[cfg(target_pointer_width = "64")]
impl Promote for u64 {
type Output = libc::c_uint;
fn promote(self) -> Self::Output {
self as Self::Output
}
}
extern "C" {
fn foo(a: u8, ...);
}
fn main() {
unsafe {
foo(1, 1u8.promote(), 1u64.promote());
}
}