How to define a function with two enum parameters where the 2nd type is determined by the 1st? - rust

I'm working on the rust-efl project, which is a binding to the Enlightenment EFL C library.
There is a function that takes two C-style enums to configure the library. I want to restrict the second argument to a certain enum based on the value of the first argument.
I'll use the actual code for this example since it makes it a bit easier to understand the use case.
Example call:
elementary::policy_set(ElmPolicy::Quit, ElmPolicyQuit::LastWindowClosed as i32);
Library Rust code:
#[repr(C)]
pub enum ElmPolicy {
/// under which circumstances the application should quit automatically. See ElmPolicyQuit
Quit = 0,
/// defines elm_exit() behaviour. See ElmPolicyExit
Exit,
/// defines how throttling should work. See ElmPolicyThrottle
Throttle
}
#[repr(C)]
pub enum ElmPolicyQuit {
/// never quit the application automatically
None = 0,
/// quit when the application's last window is closed
LastWindowClosed,
/// quit when the application's last window is hidden
LastWindowHidden
}
pub fn policy_set(policy: ElmPolicy, value: i32) {
There is an enum similar to ElmPolicyQuit for each value of ElmPolicy. The second argument to policy_set should be of the corresponding enum type.
I would like to modify policy_set so that the caller does not have to cast the value to an i32 by defining a type for value. Ideally, I would like Rust to check that the second argument is of the correct type for the given policy argument.
Implementation Attempt
I'm new to Rust, so this may be way off, but this is my current attempt:
pub fn policy_set<P: ElmPolicy, V: ElmPolicyValue<P>>(policy: P, value: V) {
unsafe { elm_policy_set(policy as c_int, value as c_int) }
}
trait ElmPolicyValue<P> {}
impl ElmPolicyValue<ElmPolicy::Quit> for ElmPolicyQuit {}
But I get this error:
src/elementary.rs:246:22: 246:31 error: ElmPolicy is not a trait
[E0404] src/elementary.rs:246 pub fn policy_set>(policy: P, value: V) {
(arrow points to first argument's type)
I made a dummy trait for ElmPolicy, but then I get
src/elementary.rs:111:21: 111:36 error: found value
elementary::ElmPolicy::Quit used as a type [E0248]
src/elementary.rs:111 impl ElmPolicyValue for
ElmPolicyQuit {}
So it seems like I can't use enums for generic types in this way.
What is the correct way to implement this?
I suppose I don't need these to actually be enums. I just need the values to be convertible to a c_int that corresponds with the EFL library's C enum.
Also, I thought about a single argument instead.
elementary::policy_set(elementary::Policy::Quit::LastWindowClosed);
But nested C-like enums don't seem to work despite documentation I found and I'm uncertain about using #[repr(C)] with nested enums.

Here's how I would do it: instead of defining ElmPolicy as an enum, I'd define it as a trait with an associated type that specifies the parameter type for the policy's value. Each policy would define a unit struct that implements ElmPolicy.
pub trait ElmPolicy {
type Value;
}
pub struct ElmPolicyQuit;
impl ElmPolicy for ElmPolicyQuit {
type Value = ElmPolicyQuitValue;
}
// TODO: ElmPolicyExit and ElmPolicyThrottle
#[repr(C)]
pub enum ElmPolicyQuitValue {
/// never quit the application automatically
None = 0,
/// quit when the application's last window is closed
LastWindowClosed,
/// quit when the application's last window is hidden
LastWindowHidden
}
pub fn policy_set<P: ElmPolicy>(policy: P, value: P::Value) {
// TODO
}
fn main() {
policy_set(ElmPolicyQuit, ElmPolicyQuitValue::LastWindowClosed);
//policy_set(ElmPolicyQuit, ElmPolicyQuitValue::LastWindowClosed as i32); // does not compile
}
You can add methods or supertraits to ElmPolicy and contraints to ElmPolicy::Value as necessary. For example, you might want to add Into<i32> as a constraint on ElmPolicy::Value, so that you can use value.into() in policy_set to convert the value to an i32.

I wouldn't try to solve both problems in one swoop. Create a straight-forward Rust binding to the C library that exposes the API as is (a *-sys package). Then provide a more idiomatic Rust API on top:
enum ElmPolicy {
Quit(QuitDetail),
Exit(ExitDetail),
Throttle(ThrottleDetail),
}
enum QuitDetail {
None,
LastWindowClosed,
LastWindowHidden,
}
Then you can create methods on ElmPolicy that produce a tuple of the C enums, or directly calls the appropriate C function, as desired.
Note that there is likely to be a bit of boilerplate as your enums in the *-sys crate will mirror those in the idiomatic API and you will want to convert between them. Macros may help reduce that.

Related

How to assign an impl trait in a struct?

Consider some struct (HiddenInaccessibleStruct) that is not accessible, but implements an API trait. The only way to obtain an object of this hidden type is by calling a function, that returns an opaque implementation of this type. Another struct owns some type, that makes use of this API trait. Right now, it seems not possible to assign this field in fn new(). The code below can also be found in rust playgrounds.
// -- public api
trait Bound {
fn call(&self) -> Self;
}
// this is not visible
#[derive(Default)]
struct HiddenInaccessibleStruct;
impl Bound for HiddenInaccessibleStruct {
fn call(&self) -> Self { }
}
// -- public api
pub fn load() -> impl Bound {
HiddenInaccessibleStruct::default()
}
struct Abc<T> where T : Bound {
field : T
}
impl<T> Abc<T> where T : Bound {
pub fn new() -> Self {
let field = load();
Abc {
field // this won't work, since `field` has an opaque type.
}
}
}
Update
The API trait Bound declares a function, that returns Self, hence it is not Sized.
There are two concepts in mid-air collision here: Universal types and existential types. An Abc<T> is a universal type and we, including Abc, can refer to whatever T actually is as T (simple as that). impl Trait-types are Rust's closest approach to existential types, where we only promise that such a type exists, but we can't refer to it (there is no T which holds the solution). This also means your constructor can't actually create a Abc<T>, because it can't decide what T is. Also see this article.
One solution is to kick the problem upstairs: Change the constructor to take a T from the outside, and pass the value into it:
impl<T> Abc<T>
where
T: Bound,
{
pub fn new(field: T) -> Self {
Abc { field }
}
}
fn main() {
let field = load();
let abc = Abc::new(field);
}
See this playground.
This works, but it only shifts the problem: The type of abc in main() is Abc<impl Bound>, which is (currently) impossible to write down. If you change the line to let abc: () = ..., the compiler will complain that you are trying to assign Abc<impl Bound> to (). If you try to comply with the advice and change the line to let abc: Abc<impl Bound> = ..., the compiler will complain that this type is invalid. So you have to leave the type of abc being implied. This brings some useability issues with Abc<impl Bound>, because you can't easily put values of that type into other structs etc.; basically, the existential type "infects" the outer type containing it.
impl Trait-types are mostly useful for immediate consumption, e.g. impl Iterator<Item=...>. In your case, with the aim apparently being to hide the type, you may get away with sealing Bound. In a more general case, it may be better to use dynamic dispatch (Box<dyn Bound>).

How can I automatically apply the From trait to convert to a generic argument type when invoking a function?

Explicitly using String::from in the following code works, but how can I make it automatically use From<OsStringWrap<'a>> trait without explicitly using String::from?
use serde::Serialize; // 1.0.115
struct OsStringWrap<'a>(&'a std::ffi::OsString);
impl<'a> From<OsStringWrap<'a>> for String {
fn from(s: OsStringWrap) -> String {
s.0.to_string_lossy().to_string()
}
}
pub fn insert<T: Serialize + ?Sized, S: Into<String>>(_key: S, _value: &T) {}
fn main() {
for (key, value) in std::env::vars_os() {
// HOW-TO: auto use From<OsStringWrap<'a>> trait
// without explicit `String::from` like below?
/*
insert(OsStringWrap(&key), &OsStringWrap(&value))
*/
// below using `String::from` to make it explicitly
// but want to find a way to make it shorter
insert(OsStringWrap(&key), &String::from(OsStringWrap(&value)))
}
}
Playground, and the insert method is a real case from tera
Your request as currently phrased is impossible. Your insert function accepts a generic type, so nothing exists to tell the compiler what type should be converted to. This tiny example is equivalent:
fn demo<T>(_: T) {}
fn main() {
demo(true.into());
}
Since the compiler can not know what concrete type to pick to convert to, the programmer must specify it.
You could potentially change your function to accept anything that can be turned into a String (e.g. T: Into<String>) or be referenced as a &str (e.g. T: AsRef<str>).
See also:
Type must be known in this context when using Iterator::collect
Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument?

How to offer an API that stores values of different types and can return them with the original type restored?

I want to offer a safe API like below FooManager. It should be able to store arbitrary user-defined values that implement a trait Foo. It should also be able to hand them back later - not as trait object (Box<dyn Foo>) but as the original type (Box<T> where T: Foo). At least conceptually it should be possible to offer this as a safe API, by using generic handles (Handle<T>), see below.
Additional criteria:
The solution should work in stable Rust (internal usage of unsafe blocks is perfectly okay though).
I don't want to modify the trait Foo, as e.g. suggested in How to get a reference to a concrete type from a trait object?. It should work without adding a method as_any(). Reasoning: Foo shouldn't have any knowledge about the fact that it might be stored in containers and be restored to the actual type.
trait Foo {}
struct Handle<T> {
// ...
}
struct FooManager {
// ...
}
impl FooManager {
// A real-world API would complain if the value is already stored.
pub fn keep_foo<T: Foo>(&mut self, foo: Box<T>) -> Handle<T> {
// ...
}
// In a real-world API this would return an `Option`.
pub fn return_foo<T: Foo>(&mut self, handle: Handle<T>) -> Box<T> {
// ...
}
}
I came up with this (Rust Playground) but not sure if there's a better way or if it's safe even. What do you think of that approach?

Is it possible to use a type to access a specific field of a Rust union?

As part of mapping a C interface to Rust, I want to handle a union that stored a few native types directly and have a pointer to an allocated type for all others.
How can I implement a parameterized wrapper type around the union that can select and use the appropriate field based on the wrapper type parameter?
In this case, I want to add a Rust wrapper that reads the data structure directly and not a solution that converts it to a Rust-native type first. Adding other support types to "trick" the compiler is fine though. The end goal is to be able to write code similar to this:
let the_list: List<i32> = get_a_list();
for i in the_list {
println!("value")
}
I am skipping the definitions of IntoIterator and friends since that boils down to accessing the correct field based on the type.
The code is highly unsafe, but we can assume that the user provides the correct type for the type parameter.
There are other solutions to this problem such as having explicit reader functions for each type, but this is focused on understanding if there are ways to make it work without those and without introducing an undue overhead.
Code that does not work, but illustrates what I want to accomplish:
#![feature(specialization)]
use std::convert::From;
union Data<T> {
int_value: i64,
ptr_value: *const T,
}
default impl<T> From<&Data<T>> for &T {
fn from(data: &Data<T>) -> &T {
&*data.ptr_value
}
}
impl From<&Data<i64>> for &i64 {
fn from(data: &Data<i64>) -> &i64 {
&*data.int_value
}
}
fn show<T>(value: &T) {
println!("value: {}", value);
}
fn main() {
let value = String::from("magic");
let data: Data<&str> = Data {
ptr_value: value.as_ptr(),
};
show(data.into());
}
I have minimized the example to avoid discussions about other aspects. This gives the following error:
error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
--> examples/union_convert.rs:10:14
|
10 | default impl<T> From<&Data<T>> for &T {
| ^ type parameter `T` must be used as the type parameter for some local type
|
= note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
= note: only traits defined in the current crate can be implemented for a type parameter
I have tried adding a wrapper around the union to handle the type-punning, but that seems to just push the error message around. Returning some other type than &T would also be possible, but I do not understand how to make it behave correctly. Using a different return type is also an option, but it still boils down to selecting the correct field based on a type.
Looking at the implementation of std::vec::Vec it does a similar thing, but in this case it always map the memory representation of the type to a real type. In this case, I want to select the correct union field based on the type that was used when writing the value.
Other similar questions that do not really answer this questions:
How to force a union to behave as if there is only one type? ask a similar question, but in this case there are explicit functions to retrieve each type and in this case I want to use the type to resolve what field to read.
Resolve union structure in Rust FFI just address the issue that you need to explicitly pick what field to read, which is what I intend to do.
How is there a conflicting implementation of From when using a generic type? This question discuss From as well, but it converting from a generic type (e.g., &T) to the implemented type (e.g., Data<T>). This question is about going in the other direction: converting from a new type (Data<T>) to a more generic type (&T).
Update: Provided a more distinct example of using into() to convert the union type one of the fields.
I would define my own trait instead of From to avoid conflicting with the standard library implementations. I'd also define a newtype wrapper / marker type. This removes the possibility of conflict when storing one of the specific types in the generic spot.
struct Other<T>(T);
union Data<T> {
bool_value: bool,
int_value: i64,
ptr_value: *const T,
}
trait Access<T> {
type Output;
unsafe fn access(&self) -> &Self::Output;
}
impl<T> Access<Other<T>> for Data<T> {
type Output = T;
unsafe fn access(&self) -> &Self::Output {
&*self.ptr_value
}
}
impl<T> Access<bool> for Data<T> {
type Output = bool;
unsafe fn access(&self) -> &Self::Output {
&self.bool_value
}
}
impl<T> Access<i64> for Data<T> {
type Output = i64;
unsafe fn access(&self) -> &Self::Output {
&self.int_value
}
}
fn main() {
let value = 123_f64;
let data: Data<f64> = Data { ptr_value: &value };
// This is safe because we just created this with
// a `f64` and nothing happened in-between.
unsafe {
println!("{}", Access::<Other<f64>>::access(&data));
}
}
See also:
How is there a conflicting implementation of `From` when using a generic type?

Is it possible to implement methods on type aliases?

Consider the following implementation:
pub struct BST {
root: Link,
}
type Link = Option<Box<Node>>;
struct Node {
left: Link,
elem: i32,
right: Link,
}
impl Link { /* misc */ }
impl BST { /* misc */ }
I keep getting the error:
cannot define inherent impl for a type outside of the crate where the type is defined; define and implement a trait or new type instead
I was able to find others had this same issue back in February, but there was seemingly no solution at the time.
Is there any fix or another way for me to implement my Link typedef in Rust?
Is there any fix
Not really. A type alias (type Foo = Bar) does not create a new type. All it does is create a different name that refers to the existing type.
In Rust, you are not allowed to implement inherent methods for a type that comes from another crate.
another way for me to implement
The normal solution is to create a brand new type. In fact, it goes by the name newtype!
struct Link(Option<Box<Node>>);
impl Link {
// methods all up in here
}
There's no runtime disadvantage to this - both versions will take the exact same amount of space. Additionally, you won't accidentally expose any methods you didn't mean to. For example, do you really want clients of your code to be able to call Option::take?
Another solution is to create your own trait, and then implement it for your type. From the callers point of view, it looks basically the same:
type Link = Option<Box<Node>>;
trait LinkMethods {
fn cool_method(&self);
}
impl LinkMethods for Link {
fn cool_method(&self) {
// ...
}
}
The annoyance here is that the trait LinkMethods has to be in scope to call these methods. You also cannot implement a trait you don't own for a type you don't own.
See also:
How do I implement a trait I don't own for a type I don't own?

Resources