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 discussFrom
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.