Parametric Types: Accept subtypes of a given type, but not the type itself - struct

consider the following code:
abstract type MyDimension end
abstract type My2D <: MyDimension end
abstract type My3D <: MyDimension end
mutable struct Shape{T<:MyDimension}
end
Currently you can declare variables of type Shape{My2D}, Shape{My3D} and Shape{MyDimension}.
Is there any way that allows the first two but not Shape{MyDimension}?

You can do:
mutable struct Shape{T<:Union{My2D, My3D}}
end
or create an intermediate type:
abstract type My2or3D <: MyDimension end
abstract type My2D <: My2or3D end
abstract type My3D <: My2or3D end
mutable struct Shape{T<:Union{My2or3D}}
end
The choice should be made considering if you want Shape to have a fixed types that it accepts or you want to potentially allow defining additional types that it accepts without having to redefine the Shape type.

Related

How to refer the struct itself in Julia

I have this code:
struct MyStruct
text::String
function MyStruct(_text::String)
text = _text
# do other things
end
end
When I wrote this, I realized that Julia is not recognizing text as MyStruct's field. How can I do something like this in Julia like in Python?
self.text = _text
Don't try to imitate Python. Julia is not object oriented.
You could emulate a Python-style constructor by
mutable struct MyStruct
text::String
function MyStruct(text::String)
self = new()
self.text = some_stuff(text)
return self
end
end
but for this to work the struct needs to be mutable. Then you can set up an uninitialize instance with new() and overwrite the fields.
Note that this is more equivalent to a combination of both __init__ and __new__. In Python, the new part is (99% of the time) already done for you, and you just mutate the already created empty object in __init__. In Julia, you have to do both on your own; this especially also requires returning the new value at the end of the constructor!
Having said all this, it's not often useful to write that way. More idiomatic would be just
struct MyStruct
text::String
MyStruct(text::String) = new(some_stuff(text))
end
unless you absolutely need the struct to be mutable (which has consequences with respect to memory layout and possible optimizations).
And also read up on the difference between inner and outer constructors. If you want the above to be the only valid way to construct MyStruct, this is fine. If you want "convenience constructors", e.g. with default arguments or conversions from other types, prefer outer constructors (where you don't have new but recursively call constructors until an inner constructor is reached).
Taking a quick glance at the constructors documentation and trying out the playground, I was able to come up with this:
struct MyStruct
text::String
function MyStruct(_text::String)
s = new(_text * "def")
# do other things
s
end
end
s = MyStruct("abc")
println(s.text) # abcdef

Function signature for generic numbers

I am trying to make a few generic functions that work on numbers, but I'm struggling with the function signatures.
Perhaps I am attacking the problem from the wrong angle, but here is where I got on my own so far. I am not hellbent on making this work this way; so if I am attacking the problem (of creating a small lib of generally useful math functions) from the wrong angle, then by all means educate me.
Let's say I want a function, add that adds up two numbers:
use std::ops::Add;
fn add(a: Add, b: Add) -> Add::Output {
a + b
}
This won't compile. Here is a playground though: https://play.integer32.com/?version=stable&mode=debug&edition=2015&gist=4589325b5c8d1f1b19440424878caa98
I get essentially two errors. The first:
error[E0393]: the type parameter `RHS` must be explicitly specified
--> src/main.rs:8:11
|
8 | fn add(a: Add, b: Add) -> Add::Output {
| ^^^ missing reference to `RHS`
|
= note: because of the default `Self` reference, type parameters must be specified on object types
I have read the chapter on advanced traits in the Rust book, so i "sort-of/kind-of" understand the RHS message, but they attack the problem of adding the Add trait to your particular data structure (a Point in the example); but never show a function signature of a function that takes anything that can be added up. So I am a little lost.
The second error:
error[E0223]: ambiguous associated type
--> src/main.rs:8:27
|
8 | fn add(a: Add, b: Add) -> Add::Output {
| ^^^^^^^^^^^ ambiguous associated type
|
= note: specify the type using the syntax `<Type as std::ops::Add>::Output`
This goes away if I write <i32 as Add>::Output, but that is not what I want. I specifically want the function to work on anything that can be added up (assuming both a and b to be the same type).
You are conflating traits and types.
Add is a trait. A trait can be implemented for a type or a class of types, but it is not a type itself.
Function arguments need to be declared with a type, not a trait. This is the main problem with your prototype – Add is not a type, so you can't use it as the type of a variable or a function argument.
Rust allows you to declare generic types, essentially type variables. You can then place trait bounds on the generic types, which require that whatever type is substituted for the generic type must implement some trait. Your example using a generic type parameter T looks like this:
fn add<T: Add>(a: T, b: T) -> T::Output
This prototype requires that a and b both have the same type T, and that T implements the Add trait.

Alias for associated type of supertrait

I have the following trait:
use std::ops::Index;
trait Map: Index<<Self as Map>::Key> {
type Key;
}
Index has the associated type Output. I semantically want this type, but I don't like the name Output in my API. Thus I would like to add an alias for that type.
I tried this (the same syntax for normal type aliases):
trait Map: Index<<Self as Map>::Key> {
type Key;
type Value = <Self as Index<Self::Key>>::Output;
}
However, this results in an error:
error[E0658]: associated type defaults are unstable (see issue #29661)
--> src/main.rs:9:9
|
9 | type Value = <Self as Index>::Output;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
From the tracking issue I could gather that this syntax is apparently used for associated types that can be overwritten by implementors. But I don't want implementors to overwrite this type, I always want Map::Value == Map::Output.
Is this possible somehow?
The associated item RFC states:
Type parameters to traits can either be "inputs" or "outputs":
Inputs. An "input" type parameter is used to determine which impl to use.
Outputs. An "output" type parameter is uniquely determined by the impl, but plays no role in selecting the impl.
The RFC also clarifies trait matching by:
Treating all trait type parameters as input types, and
Providing associated types, which are output types.
From these descriptions it is clear that an associated type is by design in control by the impl, so it is not possible to block implementors from overwriting the type.
A workaround for obtaining some form of control over the implementor may be to define a default method that uses the associated type, for example:
pub trait Map: Index<<Self as Map>::Key> {
type Key;
type Value = <Self as Index<<Self as Map>::Key>>::Output;
#[doc(hidden)]
fn invalid_operation() -> Option<&'static <Self as Index<<Self as Map>::Key>>::Output> {
None
}
}
Now for the implementors is not more possible to simply override the default Value type because the default method invalid_operation no longer typecheck.
Note also the doc(hidden) feature that strips the default method from the docs.
The hidden method name may be chosen to convey some information.
For the above example, the implementor get the error message:
error[E0399]: the following trait items need to be reimplemented as `Value` was overridden: `invalid_operation`
As you already know, assigning default associated types is not permitted in current stable Rust, a nightly version must be used and the feature has to be enabled with:
#![feature(associated_type_defaults)]

Requiring a trait bound on the associated type of an inherited trait

I have a trait Foo inheriting from another trait Bar. Bar has an associated type Baz. Foo constrains Baz such that Baz must implement Hoge.
trait Hoge {}
trait Bar {
type Baz;
}
trait Foo: Bar where Self::Baz: Hoge {}
However, when I define a generic function requiring the generic type T to implement Foo,
// [DESIRED CODE]
fn fizz<T: Foo>(buzz: T) {
// ...
}
rustc complains with EO277 unless I constrain T explicitly:
fn fizz<T: Foo>(buzz: T) where T::Baz: Hoge {
// ...
}
I do not understand why I need to do this. I would like to be able to write [DESIRED CODE]. What is the recommended way to do this?
Sadly (or not), you have to repeat the bounds.
Last year I opened a issue thinking that the type checker was being inconsistent. The code is similar to yours.
#arielb1 closed the issue and said that this was the intended behavior and gave this explanation:
The thing is that we don't want too many bounds to be implicitly
available for functions, as this can lead to fragility with distant
changes causing functions to stop compiling. There are basically 3
kinds of bounds available to a function:
bounds from explicit where-clauses - e.g. T: B when you have that clause. This includes the "semi-explicit" Sized bound.
bounds from supertraits of explicit where-clauses - a where-clause adds bounds for its supertraits (as trait B: A, the T: B bound adds a
T: A bound).
bounds from the lifetime properties of arguments (outlives/implicator/implied bounds). These are only lifetime bounds,
and irrelevant for the current problem. rust-lang/rfcs#1214 involved
them a great deal.
If your bound isn't in the list, you will have to add it explicitly if
you want to use it. I guess this should be a FAQ entry.
Today I opened an issue to request that this information to be added to the docs.
It is possible to work around this behaviour by using another associated type in Foo since the compiler accepts implicit bounds when part of the associated type definition (playground):
trait Foo: Bar<Baz = Self::HogeBaz> {
type HogeBaz: Hoge;
}
The new associated type can be hidden in a helper trait to avoid having to include it in every implementation. Full example (with renaming for clarity)
(playground)
trait Bound {
fn bound();
}
trait Trait {
type Type;
}
trait BoundedTypeHelper: Trait<Type = Self::BoundedType> {
type BoundedType: Bound;
}
impl<T> BoundedTypeHelper for T
where
T: Trait,
Self::Type: Bound,
{
type BoundedType = Self::Type;
}
trait UserTrait: BoundedTypeHelper {}
fn fizz<T: UserTrait>() {
T::Type::bound()
}
I am with you in thinking that the original where-based bound ought to be treated as part of the trait definition and applied implicitly. It feels very arbitrary that bounding associated types inline works but where clauses do not.

Why does Rust put a :: before the parameters in generics sometimes?

When declaring a variable of type vector or a hash map in Rust, we do:
let v: Vec<int>
let m: HashMap<int, int>
To instantiate, we need to call new(). However, we do so thusly:
Vec::<int>::new()
^^
HashMap::<int, int>::new()
^^
Note the sudden appearance of ::. Coming from C++, these are odd. Why do these occur? Does having a leading :: make IDENTIFIER :: < IDENTFIER … easier to parse than IDENTIFIER < IDENTIFIER, which might be construed as a less-than operation? (And thus, this is simply a thing to make the language easier to parse? But if so, why not also do it during type specifications, so as to have the two mirror each other?)
(As Shepmaster notes, often Vec::new() is enough; the type can often be inferred.)
When parsing an expression, it would be ambiguous whether a < was the start of a type parameter list or a less-than operator. Rust always assumes the latter and requires ::< for type parameter lists.
When parsing a type, it's always unambiguously a type parameter list, so ::< is never necessary.
In C++, this ambiguity is kept in the parser, which makes parsing C++ much more difficult than parsing Rust. See here for an explanation why this matters.
Anyway, most of the time in Rust, the types can be inferred and you can just write Vec::new(). Since ::< is usually not needed and is fairly ugly, it makes sense to keep only < in types, rather than making the two syntaxes match up.
The two different syntaxes don't even specify the same type parameters necessarily.
In this example:
let mut map: HashMap<K, V>;
K and V fill the type parameters of the struct HashMap declaration, the type itself.
In this expression:
HashMap::<K, V>::new()
K and V fill the type parameters of the impl block where the method new is defined! The impl block need not have the same, as many, or the same default, type parameters as the type itself.
In this particular case, the struct has the parameters HashMap<K, V, S = RandomState> (3 parameters, 1 defaulted). And the impl block containing ::new() has parameters impl<K, V> (2 parameters, not implemented for arbitrary states).

Resources