Ambiguous associated type confusion [duplicate] - rust

This question already has an answer here:
Any way to get associated type from struct?
(1 answer)
Closed 10 months ago.
So I'm making a little tictactoe to learn Rust. I define a Board which is a struct containing a 3 by 3 array called "tiles".
pub struct Board {
pub tiles: [[Option<Players>; 3]; 3],
}
Then, I try to create a method that uses the element "tiles" as a parameter
impl Board {
pub fn print_board (board: Board::tiles) {
}
However, Rust gets all offended and tells me this.
ambiguous associated typerustcE0223 game.rs(10, 32): use fully-qualified syntax: "<Board as Trait>::tiles"
First of all, I have no idea what this mean, but then I try to do as it says:
impl Board {
pub fn print_board (board: <Board as Trait>::tiles) {
}
it gives me squiggles under "Trait" where it says
failed to resolve: use of undeclared type "Trait" use of undeclared type "Trait"
Rust, o Rust what ever am I supposed to do?

Board::tiles doesn't make syntactic sense. If board is a value of type Board, then board.tiles is a data member of the struct. Neither Board::tiles nor board.tiles is a type so you can't use it for defining the type of an argument.
You can either take the type of the data member explicitly, like this: pub fn print_board(board: [[Option<Players>; 3]; 3]).
Or if you don't want to write out that type again and again you can use a type alias like this
pub type TileArray = [[Option<Players>; 3]; 3];
pub struct Board {
pub tiles: TileArray,
}
impl Board {
pub fn print_board(board: TileArray) {
// ...
}
}
The error message is confusing to you because you're specifying a type of a function parameter, but attempting to use the name of a value to do that. Your syntax is just completely invalid. The syntax SomeType::Something can make some sense, where Something is an associated type of some trait implemented by SomeType. This is the compiler's best guess of making sense of the syntax you gave it, so it's trying to correct you based on that. That syntax still wouldn't be correct, since you'd need the fully-qualified syntax indicated by the error message.

Related

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?

Rust subtyping/"runtime polymorphism"/using traits in Boxes

Consider the following dumbed-down example:
pub trait ThemePark<A, V>
where
A: Attraction,
V: Visitor,
{
fn create(square_size: u32, name: &str) -> Self;
fn get_attractions(&self) -> Vec<A>;
fn get_visitors(&self) -> Vec<V>;
}
pub trait Attraction {}
pub trait Visitor {}
A concrete ThemePark could have an arbitrary set of attractions, as well as visitors. They are implemented with structs:
struct ElderlyWhiteMale;
impl Visitor for ElderlyWhiteMale {}
A ThemePark is wrapped in some company's assets, like so:
pub struct Asset<'a> {
name: &str,
theme_park: Box<ThemePark<> + 'a> // <-- This won't compile, as ThemePark needs 2 type arguments
}
This begins my pain. I put ThemePark in a Box because I don't know the size of it at compile time. It could be wrapped around any kind of Attraction and Visitor.
ThemePark needs 2 type arguments, but I can't know them at compile-time. Somewhere in my code I read this from an external file and build a ThemePark accordingly.
The idea is, that at runtime I can create a ThemePark from an external source and then invoke the functions defined in the trait on it.
impl Asset {
fn init_and_query() -> () {
let theme_park: Box<ThemePark> = match external_file.get_theme_park_type {
ThemeParkType::FUN => unimplemented! {"There is no fun, yet!"},
ThemeParkType::SERIOUS => {
println!("Creating serious themepark");
SeriousThemePark::create(size /*...*/)
}
};
let attractions = theme_park.get_attractions();
// ... Do something with the attractions
}
}
pub enum ThemeParkType {
FUN,
SERIOUS,
}
I understand that I can't put the ThemePark as-is on the stack... it's size is unknown at compile time, so the compiler can't know what to allocate.
That's why I either use a reference & or wrap it in a Box like I do here.
I understand there is type erasure, meaning that I would get back only a ThemePark and not a SeriousThemePark, but that would suffice for the moment.
Am I using traits all wrong here? How would you go and fix that. Coming from Java/Scala/C++ I seem to be stuck too deep in existing thinking.
Rust polymorphism is very similar to C++ polymorphism in that regard; it features:
compile-time polymorphism, to parameterize an item with types known at compile-time,
run-time polymorphism, when the concrete types are not known at compile-time.
Rust uses trait to define an interface which is then used to both constrain compile-time type parameters and serve as base-class/interface for run-time polymorphism, which is perhaps where your confusion comes from, however both kinds of polymorphism are inherently different.
pub struct Asset<'a> {
name: &str,
theme_park: Box<ThemePark<> + 'a> // <-- This won't compile, as ThemePark needs 2 type arguments
}
Then you should NOT be using compile-time polymorphism, and instead define ThemePark as:
pub trait ThemePark {
fn create(square_size: u32, name: &str) -> Self;
fn get_attractions(&self) -> Vec<Box<Attraction>>;
fn get_visitors(&self) -> Vec<Box<Visitor>>;
}
By instantiating a ThemePark<A, V> you create a theme park which can only ever contain one type of attractions (it's all Haunted Houses here, no Flume Ride sorry!) and one type of visitors (only Elderly Guys in, no Elderly Ladies or Kids).

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

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.

Implementing a trait that has associated trait types

I'm having trouble learning about associated types. My problem code:
trait Fooer {
fn foo(&self);
}
trait FooStore {
type T: Fooer;
fn store_foo(&self, fooer: Self::T);
}
#[allow(dead_code)]
struct DB {}
impl FooStore for DB {
type T = Fooer;
fn store_foo(&self, _fooer: Self::T) {}
}
fn main() {}
Play link
The intent here is to use associated types to make the FooStore trait not require the awkward and problematic syntax of impl<F:Fooer, T: FooStore<F>> FooStore<F> for DB because that often complains about F not being used.
However, the official docs on this feature show objects implementing the underlying associated type - but not traits. In this example, DB does not know what structs might be passed into store_foo(..), so it needs to use a trait to solve this issue.
With that said, how can I get an associated type to use a trait during impl? That is, how can I write type T = Fooer;? Or am I using this wrong somehow?
Note: I'm having some trouble constructing this example, I'm trying to correct this now. The error I was having is:
cargo: the trait `Fooer` cannot be made into an object [E0038]
The intent here is to use associated types to make the FooStore trait not require the awkward and problematic syntax of impl<F:Fooer, T: FooStore<F>> FooStore<F> for DB because that often complains about F not being used.
Your struct DB needs to assign a concrete type that implements Fooer to FooStore::T. Fooer is a trait, but can also be used as an unsized type. However, you can't use an unsized type here, because you can't pass an argument of an unsized type by value (which FooStore::store_foo requires).
If you don't want DB to assign a particular type to FooStore::T, then you can make DB generic.
use std::marker::PhantomData;
#[allow(dead_code)]
struct DB<F: Fooer> {
_phantom: PhantomData<F>,
}
impl<F: Fooer> FooStore for DB<F> {
type T = F;
fn store_foo(&self, _fooer: Self::T) {}
}
Notice the use of PhantomData: we use it to force the parameter T to be used, and it also indicates that DB<T> conceptually owns objects of type T.

Is there an elegant way to make a generic tuple struct with an unused type without PhantomData?

I'd like to create a generic tuple struct Producer which can hold any type P which implements the trait Produce<T>, defined below. This produces the (expected) commented error:
trait Produce<T> {
fn get(&mut self) -> T;
}
// ERROR: parameter `T` is never used [E0392]
struct Producer<P,T>(P) where P: Produce<T>;
If this were a non-tuple struct, I could remedy this issue by adding a PhantomData<T> field and writing a constructor Producer::new(p: P) to hide this as an implementation detail. However, I'm using this type as one of a family of tuple structs in a Builder API, so using a conventional struct + constructor feels pretty out of place.
Is there any way to achieve this?
In many cases, you don't want to parameterize your trait, but instead want an associated type:
trait Produce {
type T;
fn get(&mut self) -> Self::T;
}
struct Producer<P>(P) where P: Produce;
fn main() {}
Unfortunately, it's tough to tell if this will work for you without knowing a lot more about the anticipated use case and code examples, which might be too verbose for Stack Overflow.

Resources