Function signature for generic numbers - rust

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.

Related

Why do we need to define the generic type with the function name other than the parameter signatures in Rust?

Going over the generic types chapter of the official Rust book, I noticed that we have to define the generic function as follows:
fn largest<T>(list: &[T]) -> T {
My doubt is: why add <T> right after largest? We don't do that for non-generic functions such as: fn largest(list: &[i32]) -> i32.
They mean two different (and equally valid) things
fn largest<T>(list: &[T]) -> T
This says "largest is a function that, for any type T, takes a slice of T and returns a T". On the other hand,
fn largest(list: &[T]) -> T
This function says "largest takes a slice of T and returns a T", where T is some specific type in scope. That is, this second definition assumes there's something of the form
struct T {}
or some other type declaration, alias, or import in scope right now, and largest only works for that type. Note that T is a horrible name for a specific type, but Rust doesn't care about good names. As far as Rust is concerned, T is a perfectly valid type. So is ndscjkdbhsgey, but please don't name your structs that either. We need the <T> to tell Rust "this is not a specific type name; it's a variable, and I'm choosing to call it T".
Because you have to create the generic type T. i32 is a predefined type while T is one that only exists within the context of that function and you have to define it like a variable for the compiler. It does not have to be T and you could have made the function definition be fn largest<U>(list: &[U]) -> U.
I'd say the <T> syntax introduces the type T in scope so it can be used for parameter types and return type. If you didn't mark the function with <T> how would it know what T is? It would rely on the type being visible already which is the case for the non-generic function you provided - i32 is imported by default.
For the same reason we write <T> after generic implementations. For example impl<T> SomeGenericStruct<T>. If you didn't mark the implementation, or in the case of your question, your function, as a generic with the <T> syntax, which is also required of lifetimes such as <&'a T>, then the compiler wouldn't treat T as a generic.

How do I declare a generic function which can add together references to sparse vectors? [duplicate]

This question already has answers here:
How to write a trait bound for adding two references of a generic type?
(1 answer)
How do I require a generic type implement an operation like Add, Sub, Mul, or Div in a generic function?
(2 answers)
Closed 4 years ago.
I'm trying to use the sprs crate (version 0.6.3) to manipulate sparse vectors. I'd like to add two vectors together.
I started off with an implementation of the Add trait, then simplified this to an implementation function. Finally, I've boiled down the problem to a simple generic function.
// This works: the scalar type `u64` is fixed here
fn adder(first: &CsVec<u64>, second: &CsVec<u64>) -> CsVec<u64> {
first + second
}
// When I try to make the scalar type generic, it doesn't work
fn adder2<T>(first: &CsVec<T>, second: &CsVec<T>) -> CsVec<T>
where
CsVec<T>: Add,
T: Add + Debug,
{
first + second
}
The first version compiles fine, but I'd like to know why the second version won't compile. I get this error message:
error[E0369]: binary operation `+` cannot be applied to type `&sprs::sparse::CsVecBase<std::vec::Vec<usize>, std::vec::Vec<T>>`
--> libp3prime/src/lib/datacache.rs:62:5
|
62 | first + second
| ^^^^^^^^^^^^^^
|
= note: an implementation of `std::ops::Add` might be missing for `&sprs::sparse::CsVecBase<std::vec::Vec<usize>, std::vec::Vec<T>>`
I don't really understand the error message. I know that you can add two CsVecs together, since adder() compiles, so I am a bit lost.
The two vectors should add together.
Be sure that the trait bounds defined on the function match the behavior used in the function.
first and second are not CsVec<T>, but &CsVec<T>. In Rust, &X is a different type from X. You need a trait bound that says that you can add two &CsVec<T>s and get a CsVec<T> as output:
fn adder2<'a, T>(first: &'a CsVec<T>, second: &'a CsVec<T>) -> CsVec<T>
where
&'a CsVec<T>: Add<Output = CsVec<T>>,
{
first + second
}
No bounds on T are needed in this example.
The 'a lifetime parameter in this case was passed in to the function. Sometimes it is useful to define a trait bound on a reference inside the function, for example, to use + on references to local variables. In that case you would want to use the higher-ranked trait bound for<'a> &'a CsVec<T>: Add<Output = CsVec<T>> instead. See the linked questions below for more information.
Lukas Kalbertodt points out that it may sometimes be more flexible to say "I just want to add two &CsVec<T>s, and I will return whatever type that operation gives me", which you can do by returning <&'a CsVec<T> as Add>::Output:
fn adder2<'a, T>(first: &'a CsVec<T>, second: &'a CsVec<T>) -> <&'a CsVec<T> as Add>::Output
where
&'a CsVec<T>: Add,
{
first + second
}
In this case the output type does not have to be exactly CsVec<T>, but when it is, it works the same way as the first version.
Related
How to write a trait bound for adding two references of a generic type?
How do I require a generic type implement an operation like Add, Sub, Mul, or Div in a generic function?

Can't implement a trait I don't own for all types that implement a trait I do own

pub trait AllValues {
fn all_values() -> Vec<Self> where Self: std::marker::Sized;
}
use rand::Rand;
use rand::Rng;
impl<T: AllValues + Sized> Rand for T {
fn rand<R: Rng, T>(rng: &mut R) -> T {
let values = T::all_values();
let len = values.len();
if len == 0 {
panic!("Cannot pick a random value because T::all_values() returned an empty vector!")
} else {
let i = rng.gen_range(0, len);
values[i]
}
}
}
The preceding code produces the following compile-time error:
error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g. `MyStruct<T>`); only traits defined in the current crate can be implemented for a type parameter
--> src/lib.rs:137:1
|
137 | impl<T: AllValues + Sized> Rand for T {
| ^
According to the restrictions on implementing traits mentioned here I should be able to implement Rand for AllValues since AllValues is defined in my crate. Is this actually allowed by the coherence/orphan impls rules? And if so, what is the right way to implement Rand for things that implement AllValues?
I should be able to implement Rand for AllValues since AllValues is defined in my crate.
No, you are only allowed to implement your own trait AllValues for types you didn't define. You can't make the logical jump to implementing an unrelated trait that you also didn't define.
There are two considerations to remember:
If your trait is public (which it is based on the code you've provided), you aren't the only one that can implement the trait. Consumers of your crate might be able to implement it for their own types, where they might also decide to implement Rand!
The rand crate might decide to implement Rand for T some time in the future.
What is the right way to implement Rand for things that implement AllValues?
I don't believe there is one. I'd just introduce a wrapper type that holds a value or a reference to a value that implements your trait and implement Rand for that.
See also:
How do I implement a trait I don't own for a type I don't own?
I see now where my mistake in interpretation was. Quoting from the the traits section of the book:
There’s one more restriction on implementing traits: either the trait
or the type you’re implementing it for must be defined by you. Or more
precisely, one of them must be defined in the same crate as the impl
you're writing.
(Emphasis added.)
Since I was trying to implement a trait I must have read that as "either the trait or the trait you’re implementing it for". This discussion about an eventually implemented rfc specifically mentions a similar case to the one I presented : impl<T: Copy> Clone for T as something that would not be allowed.
Creating a wrapper type as suggested elsewhere is one solution for this problem. Assuming the ownership of the type implementations allows it, implementing the trait explicitly for each concrete instance, (optionally condensing the code with a macro,) as suggested here is another.

Why doesn't `Box::into_raw` take `self` as parameter?

This simple program:
fn main() {
let b: Box<i32> = Box::new(1);
b.into_raw();
}
Produces this inconvenient error when compiled with Rust 1.12.0:
error: no method named `into_raw` found for type `Box<i32>` in the current scope
--> <anon>:3:7
|
3 | b.into_raw();
| ^^^^^^^^
|
= note: found the following associated functions; to be used as methods, functions must have a `self` parameter
= note: candidate #1 is defined in an impl for the type `Box<_>`
This is because into_raw is not defined to take self as parameter, but instead is defined as:
impl Box<T: ?Sized> {
fn into_raw(b: Box<T>) -> *mut T;
}
This seems inconvenient, and I cannot find a rationale.
So... why?
Because 99.995% of the time (statistic totally made up), you expect method calls to happen to the thing being pointed to, not to the pointer itself. As a result, the "smart pointer" types in Rust generally avoid doing anything to break that expectation. An obvious exception would be something like Rc/Arc implementing Clone directly.
Box implements Deref, which means that all methods that are enclosed by the Box are automatically made available; from the outside, Box<T> and T look and act the same.
If into_raw were a method instead of an associated function, it would shadow any into_raw method on the contained type.
There are other examples of these enhancing associated functions on Rc, such as downgrade or try_unwrap, or on Arc, such as make_mut.

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.

Resources