Variadic generic trait paramterised on same type in Rust - rust

I would broadly like to write something like this:
pub struct Example<..T> {...}
That is, parametrise Example over any number of type T. This is discussed in an RFC, but this seems to be quite stale.
I know that we can have variadic functions by using c_variadic. Is there any way to expand this to structs?
Edit to include more concrete example:
What I am trying to do is (may not be valid Rust, but just an example):
// `a` and `b` are some structs that communicate via channels
// Label is some label we can match on
// Continuation is a trait
let options = vec![(label1, cont1), (label2, cont2)...];
let a = Offer<...(Label, Continuation)>::new();
let b = Pick<Label>::new();
// offer and pick are just some functions that communicate
// the choice via channels
// The important part being that offer and pick would be parametrised
// over <..(Label, Continuation)>
let picked = a.offer(options);
let picked = b.pick(label1);

Perhaps you want a const size parameter:
struct Offer<T, const U: usize> {
contents: [T; U]
}
This can represent a list of any size. All items must be of the same type though but in your example this seems to be the case.

Related

Rust create a vector of custom struct type

I'm using config-rs crate. I want to set an array to a default value in my configuration. I want to use set_default() method from the crate that accepts an argument of type Value<Array> https://docs.rs/config/latest/config/builder/struct.ConfigBuilder.html#method.set_default
I'm new to rust and having a hard time as to how I can quickly create a vector<Value>
let default: Vec<ValueKind>= vec![1,2,3];// Errors
let default: Vec<Value>= vec![1,2,3]; // Errros
If you want to create a Vec<T>, the items have to be of type T. Rust does not perform implicit conversions so you need to write a little more code to make this work. It won't even allow implicit conversions between primitive types (Ex: i32 to i64).
For this, you can create a new Value or ValueKind by using From. I know this can be done because the documentation states that ValueKind implements From<i32>. The same thing also applies to Value, but it is much harder to spot when reading through the documentation.
// Same applies to `Value`.
let default: Vec<ValueKind> = vec![ValueKind::from(1), ValueKind::from(2), ValueKind::from(3)];
To make this easier, you also have a few other options. Into is implemented automatically for all types implimenting From, we can replace the lengthy ValueKind::from(1) with a shorter 1.into() call.
let default: Vec<ValueKind> = vec![1.into(), 2.into(), 3.into()];
If you have a lot of values you could also choose to use an iterator to convert an array of values all in one go.
let default: Vec<ValueKind> = [1, 2, 3, 4, 5, 6, 7, 8, 9]
.into_iter()
.map(|x| ValueKind::from(x))
.collect();
That being said, I am somewhat surprised the crate authors did not give the option to use Vec<i32> as-is (Though I have not checked the documentation on this since I do not know how it is used)*. It is not too difficult to write a generic function which accepts any iterable type like a Vec, array, or a bunch of other things, and turn it into ValueKind or Value as it goes.
/// Takes some iterable value and turns it into a Vec<ValueKind>.
pub fn to_valuekind<A, T>(values: A) -> Vec<ValueKind>
where
A: IntoIterator<Item = T>,
ValueKind: From<T>,
{
values.into_iter().map(|x| ValueKind::from(x)).collect()
}
*Looks like it is a restriction on how set_default functions. It accepts any Into<Value>, but by doing so they can't also implement across IntoIterator since it could theoretically cause a conflict if a type implemented both Into<Value> and IntoIterator<T> where T: Into<Value>.

Pass struct generic type to trait generic method [duplicate]

In this question, an issue arose that could be solved by changing an attempt at using a generic type parameter into an associated type. That prompted the question "Why is an associated type more appropriate here?", which made me want to know more.
The RFC that introduced associated types says:
This RFC clarifies trait matching by:
Treating all trait type parameters as input types, and
Providing associated types, which are output types.
The RFC uses a graph structure as a motivating example, and this is also used in the documentation, but I'll admit to not fully appreciating the benefits of the associated type version over the type-parameterized version. The primary thing is that the distance method doesn't need to care about the Edge type. This is nice but seems a bit shallow of a reason for having associated types at all.
I've found associated types to be pretty intuitive to use in practice, but I find myself struggling when deciding where and when I should use them in my own API.
When writing code, when should I choose an associated type over a generic type parameter, and when should I do the opposite?
This is now touched on in the second edition of The Rust Programming Language. However, let's dive in a bit in addition.
Let us start with a simpler example.
When is it appropriate to use a trait method?
There are multiple ways to provide late binding:
trait MyTrait {
fn hello_word(&self) -> String;
}
Or:
struct MyTrait<T> {
t: T,
hello_world: fn(&T) -> String,
}
impl<T> MyTrait<T> {
fn new(t: T, hello_world: fn(&T) -> String) -> MyTrait<T>;
fn hello_world(&self) -> String {
(self.hello_world)(self.t)
}
}
Disregarding any implementation/performance strategy, both excerpts above allow the user to specify in a dynamic manner how hello_world should behave.
The one difference (semantically) is that the trait implementation guarantees that for a given type T implementing the trait, hello_world will always have the same behavior whereas the struct implementation allows having a different behavior on a per instance basis.
Whether using a method is appropriate or not depends on the usecase!
When is it appropriate to use an associated type?
Similarly to the trait methods above, an associated type is a form of late binding (though it occurs at compilation), allowing the user of the trait to specify for a given instance which type to substitute. It is not the only way (thus the question):
trait MyTrait {
type Return;
fn hello_world(&self) -> Self::Return;
}
Or:
trait MyTrait<Return> {
fn hello_world(&Self) -> Return;
}
Are equivalent to the late binding of methods above:
the first one enforces that for a given Self there is a single Return associated
the second one, instead, allows implementing MyTrait for Self for multiple Return
Which form is more appropriate depends on whether it makes sense to enforce unicity or not. For example:
Deref uses an associated type because without unicity the compiler would go mad during inference
Add uses an associated type because its author thought that given the two arguments there would be a logical return type
As you can see, while Deref is an obvious usecase (technical constraint), the case of Add is less clear cut: maybe it would make sense for i32 + i32 to yield either i32 or Complex<i32> depending on the context? Nonetheless, the author exercised their judgment and decided that overloading the return type for additions was unnecessary.
My personal stance is that there is no right answer. Still, beyond the unicity argument, I would mention that associated types make using the trait easier as they decrease the number of parameters that have to be specified, so in case the benefits of the flexibility of using a regular trait parameter are not obvious, I suggest starting with an associated type.
Associated types are a grouping mechanism, so they should be used when it makes sense to group types together.
The Graph trait introduced in the documentation is an example of this. You want a Graph to be generic, but once you have a specific kind of Graph, you don't want the Node or Edge types to vary anymore. A particular Graph isn't going to want to vary those types within a single implementation, and in fact, wants them to always be the same. They're grouped together, or one might even say associated.
Associated types can be used to tell the compiler "these two types between these two implementations are the same". Here's a double dispatch example that compiles, and is almost similar to how the standard library relates iterator to sum types:
trait MySum {
type Item;
fn sum<I>(iter: I)
where
I: MyIter<Item = Self::Item>;
}
trait MyIter {
type Item;
fn next(&self) {}
fn sum<S>(self)
where
S: MySum<Item = Self::Item>;
}
struct MyU32;
impl MySum for MyU32 {
type Item = MyU32;
fn sum<I>(iter: I)
where
I: MyIter<Item = Self::Item>,
{
iter.next()
}
}
struct MyVec;
impl MyIter for MyVec {
type Item = MyU32;
fn sum<S>(self)
where
S: MySum<Item = Self::Item>,
{
S::sum::<Self>(self)
}
}
fn main() {}
Also, https://blog.thomasheartman.com/posts/on-generics-and-associated-types has some good information on this as well:
In short, use generics when you want to type A to be able to implement a trait any number of times for different type parameters, such as in the case of the From trait.
Use associated types if it makes sense for a type to only implement the trait once, such as with Iterator and Deref.

How to handle generic types with different concrete types in rust efficiently?

The main goal is to implement a computation graph, that handles nodes with values and nodes with operators (think of simple arithmetic operators like add, subtract, multiply etc..). An operator node can take up to two value nodes, and "produces" a resulting value node.
Up to now, I'm using an enum to differentiate between a value and operator node:
pub enum Node<'a, T> where T : Copy + Clone {
Value(ValueNode<'a, T>),
Operator(OperatorNode)
}
pub struct ValueNode<'a, T> {
id: usize,
value_object : &'a dyn ValueType<T>
}
Update: Node::Value contains a struct, which itself contains a reference to a trait object ValueType, which is being implemented by a variety of concrete types.
But here comes the problem. During compililation, the generic types will be elided, and replaced by the actual types. The generic type T is also being propagated throughout the computation graph (obviously):
pub struct ComputationGraph<T> where T : Copy + Clone {
nodes: Vec<Node<T>>
}
This actually restricts the usage of ComputeGraph to one specific ValueType.
Furthermore the generic type T cannot be Sized, since a value node can be an opqaue type handled by a different backend not available through rust (think of C opqaue types made available through FFI).
One possible solution to this problem would be to introduce an additional enum type, that "mirrors" the concrete implementation of the valuetype trait mentioned above. this approach would be similiar, that enum dispatch does.
Is there anything I haven't thought of to use multiple implementations of ValueType?
update:
What i want to achive is following code:
pub struct Scalar<T> where T : Copy + Clone{
data : T
}
fn main() {
let cg = ComputeGraph::new();
// a new scalar type. doesn't have to be a tuple struct
let a = Scalar::new::<f32>(1.0);
let b_size = 32;
let b = Container::new::<opaque_type>(32);
let op = OperatorAdd::new();
// cg.insert_operator_node constructs four nodes: 3 value nodes
// and one operator nodes internally.
let result = cg.insert_operator_node::<Container>(&op, &a, &b);
}
update
ValueType<T> looks like this
pub trait ValueType<T> {
fn get_size(&self) -> usize;
fn get_value(&self) -> T;
}
update
To further increase the clarity of my question think of a small BLAS library backed by OpenCL. The memory management and device interaction shall be transparent to the user. A Matrix type allocates space on an OpenCL device with types as a primitive type buffer, and the appropriate call will return a pointer to that specific region of memory. Think of an operation that will scale the matrix by a scalar type, that is being represented by a primitive value. Both the (pointer to the) buffer and the scalar can be passed to a kernel function. Going back to the ComputeGraph, it seems obvious, that all BLAS operations form some type of computational graph, which can be reduced to a linear list of instructions ( think here of setting kernel arguments, allocating buffers, enqueue the kernel, storing the result, etc... ). Having said all that, a computation graph needs to be able to store value nodes with a variety of types.
As always the answer to the problem posed in the question is obvious. The graph expects one generic type (with trait bounds). Using an enum to "cluster" various subtypes was the solution, as already sketched out in the question.
An example to illustrate the solution. Consider following "subtypes":
struct Buffer<T> {
// fields
}
struct Scalar<T> {
// fields
}
struct Kernel {
// fields
}
The value containing types can be packed into an enum:
enum MemType {
Buffer(Buffer<f32>);
Scalar(Scalar<f32>);
// more enum variants ..
}
Now MemType and Kernel can now be packed in an enum as well
enum Node {
Value(MemType);
Operator(Kernel);
}
Node can now be used as the main type for nodes/vertices inside the graph. The solution might not be very elegant, but it does the trick for now. Maybe some code restructuring might be done in the future.

Is it possible to make my own Box-like wrapper?

I noticed that Box<T> implements everything that T implements and can be used transparently. For Example:
let mut x: Box<Vec<u8>> = Box::new(Vec::new());
x.push(5);
I would like to be able to do the same.
This is one use case:
Imagine I'm writing functions that operate using an axis X and an axis Y. I'm using values to change those axis that are of type numbers but belongs only to one or the other axis.
I would like my compiler to fail if I attempt to do operations with values that doesn't belong to the good axis.
Example:
let x = AxisX(5);
let y = AxisY(3);
let result = x + y; // error: incompatible types
I can do this by making a struct that will wrap the numbers:
struct AxisX(i32);
struct AxisY(i32);
But that won't give me access to all the methods that i32 provides like abs(). Example:
x.abs() + 3 // error: abs() does not exist
// ...maybe another error because I don't implement the addition...
Another possible use case:
You can appropriate yourself a struct of another library and implement or derive anything more you would want. For example: a struct that doesn't derive Debug could be wrapped and add the implementation for Debug.
You are looking for std::ops::Deref:
In addition to being used for explicit dereferencing operations with the (unary) * operator in immutable contexts, Deref is also used implicitly by the compiler in many circumstances. This mechanism is called 'Deref coercion'. In mutable contexts, DerefMut is used.
Further:
If T implements Deref<Target = U>, and x is a value of type T, then:
In immutable contexts, *x on non-pointer types is equivalent to *Deref::deref(&x).
Values of type &T are coerced to values of type &U
T implicitly implements all the (immutable) methods of the type U.
For more details, visit the chapter in The Rust Programming Language as well as the reference sections on the dereference operator, method resolution and type coercions.
By implementing Deref it will work:
impl Deref for AxisX {
type Target = i32;
fn deref(&self) -> &i32 {
&self.0
}
}
x.abs() + 3
You can see this in action on the Playground.
However, if you call functions from your underlying type (i32 in this case), the return type will remain the underlying type. Therefore
assert_eq!(AxisX(10).abs() + AxisY(20).abs(), 30);
will pass. To solve this, you may overwrite some of those methods you need:
impl AxisX {
pub fn abs(&self) -> Self {
// *self gets you `AxisX`
// **self dereferences to i32
AxisX((**self).abs())
}
}
With this, the above code fails. Take a look at it in action.

In Rust, is there any way to change a struct/type to be "Send"?

I've hit the issue a couple of times of trying to pass things into the spawn function (to create a new thread/task) and have the compiler tell me error: cannot capture variable of type "blah blah", which does not fulfill "Send", in a bounded closure.
Is there a way to transform a type be able to fulfill "Send" or is that fixed based on some set of rules?
For example, I can easily implement the ToStr trait by using a directive like this:
#[deriving(ToStr, Rand)]
struct Point {
x: int,
y: int,
}
Can I do something similar for the Send trait? Or are "kind" Traits treated differently?
Here's a concrete example of this issue - is there some way to overcome it?
fn create_process_options(cmdinfo: &CmdInfo) -> (ProcessOptions, Option<FileDesc>) {
// ... omitted
}
// "po" is of type std::run::ProcessOptions
let (po, filedesc_opt) = create_process_options(&cmdinfo);
spawn(proc() {
let mut ps = Process::new(cmdinfo.program, cmdinfo.args, po).expect("darn");
ps.finish();
});
Compiler error:
error: cannot capture variable of type `std::run::ProcessOptions<>`, which does not fulfill `Send`, in a bounded closure
let mut process = Process::new(cmdinfo.program, cmdinfo.args, po).expect("darn");
^~
note: this closure's environment must satisfy `Send`
let mut process = Process::new(cmdinfo.program, cmdinfo.args, po).expect("darn");
Send is a rust Kind, the other things you mentioned are Traits. While both can be used to bound generics, they are in fact quite different. You have to opt-in to a Trait, but what Kinds a type has are inferred based on their contents - besides changing the contents you can't change the Kind of a type.
For most Kinds, the rule is "a type X is of Kind Y if all the members of X are of Kind Y."
In this case, since being Send requires that you fulfill 'static, which means that they don't contain any non-'static references. Since ProcessOptions contains a non-static lifetime Option<&'a Path>, like Chris Morgan detailed in his comment, ProcessOptions does not fulfill Send.

Resources