I'm trying to translate some of my C++ code into Rust and came across the following problem. The code is simplified to a (hopefully) minimal not-working example. In the end it is supposed to work with all unsigned integer types, but for this example it just needs to implement the PartialOrd trait.
#![allow(incomplete_features)]
#![feature(generic_const_exprs)]
#![feature(const_trait_impl)]
const fn foo<T>(n: T, k: T) -> T
where
T: Sized,
T: Copy,
T: ~const core::marker::Destruct,
T: ~const std::cmp::PartialOrd,
{
if n < k {
n
} else {
k
}
}
Fails to compile with the following error message:
error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability
13 | if n < k {
| ^
If I replace the arguments n and k with references, it compiles. Turns out, the functions in the PartialOrd are also implemented using references. So from my understanding, the expression 2 < 4 will call the le function with references. For performance reasons, I doubt that's what's happening, though.
It's actually two questions that I'm asking:
Can I solve the error without using references?
Why is PartialOrd using references while Ord uses values (at least for min and max, but not for cmp)?
Adding #![feature(const_refs_to_cell)] makes it compile.
min and max take Self because they return one of the two arguments as Self. Other methods in Ord and PartialOrd else returns a bool or an Ordering so they can take by reference. if min and max they took by reference the signature would be fn max<'a>(&'a self, other: &'a Self) -> &'a Self which you "get for free" because there is a impl<A> Ord for &A where A: Ord.
Related
Consider the following Rust function, which is meant to indicate whether the given 3-byte string is equal to b"foo".
fn is_foo(value: [u8; 3]) -> bool {
value == b"foo"
}
This doesn't work:
error[E0277]: can't compare [u8; 3] with &[u8; 3]
The compiler is complaining that it can't compare a value of some type to a reference of the same type.
I found two ways of getting the equality check to work:
Turning the value into a ref first: &value == b"foo"
Turning the ref into a value first: value == *b"foo"
Coming from C++ (where a value and a reference are pretty much the same thing), both approaches look a bit strange to me. What is the most idiomatic way of comparing a value and a reference?
In rust T and &T (and also &mut T) are different types. Though different types can be compared it is not standard. Equality comparison (the == operator) is done by PartialEq trait. It looks like this:
pub trait PartialEq<Rhs = Self> where
Rhs: ?Sized,
{
fn eq(&self, other: &Rhs) -> bool;
fn ne(&self, other: &Rhs) -> bool { ... }
}
And although it is generic over Rhs (meaning that one type can potentially be compared with many other types), it defaults to Self.
So coming back to your example you could just write value.eq(b"foo") (that would compare values of references), but though it is not wrong, probably more often is &value == b"foo". Dereferencing is fine too, but I seldom see it.
You also don't have to be worry, that equality between types and their references will differ, because standard library has following blanket implementation (and others for mutable references):
impl<A, B> PartialEq<&B> for &A where
A: PartialEq<B> + ?Sized,
B: ?Sized,
{ ... }
That automatically implements equality for references in a proper way.
As part of a "learn Rust" project, I've been working through some Project Euler problems, in which it would be convenient to have a few generic math functions. As an example, let's say I want to write a generic "square" function. If I can live with the builtin numeric types (all of which I believe are Copy), I can write this:
fn square<A>(n: A) -> A
where
A: Mul<Output = A> + Copy,
{
n.mul(n)
}
This seems to work fine. But what if I want to use a numeric type that is not Copy? Say I'm using a bignum library in which numbers are not Copy, but which implements std::ops::Mul. I would have thought I could do this:
fn square_ref<'a, A>(n: &'a A) -> A
where
A: Mul<&'a A, Output = A>,
{
n.mul(n)
}
but that gets me the following error:
error[E0507]: cannot move out of `*n` which is behind a shared reference
--> src/main.rs:16:5
|
16 | n.mul(n)
| ^ move occurs because `*n` has type `A`, which does not implement the `Copy` trait
error: aborting due to previous error
Why does the call to mul insist on resolving to type A, instead of &A? Just to be clear, my question isn't really about generic math in Rust -- I'm hoping that figuring out how to do this sort of thing, or learning why it can't be done, will help me better understand the language in general.
You need to bound &'a A to be Mul, not A:
use std::ops::Mul;
fn square_ref<A>(n: &A) -> A
where
// note the &'a A instead of just A
for<'a> &'a A: Mul<&'a A, Output = A>,
{
n.mul(n)
}
I have a simple struct that I would like to implement Index for, but as a newcomer to Rust I'm having a number of troubles with the borrow checker. My struct is pretty simple, I'd like to have it store a start and step value, then when indexed by a usize it should return start + idx * step:
pub struct MyStruct {
pub start: f64,
pub step: f64,
}
My intuition is that I'd simply be able to take the signature of Index and plug in my types:
impl Index<usize> for MyStruct {
type Output = f64;
fn index(&self, idx: usize) -> &f64 {
self.start + (idx as f64) * self.step
}
}
This gives the error mismatched types saying expected type &f64, found type f64. As someone who has yet to fully understand how Rust's type system works, I tried simply slapping & on the expression:
fn index(&self, idx: usize) -> &f64 {
&(self.start + (idx as f64) * self.step)
}
This now tells me that the borrowed value does not live long enough, so maybe it needs a lifetime variable?
fn index<'a>(&self, idx: usize) -> &'a f64 {
&(self.start + (idx as f64) * self.step)
}
The error is the same, but the note now gives lifetime 'a instead of lifetime #1, so I guess that's not necessary, but at this point I feel like I'm stuck. I'm confused that such a simple exercise for most languages has become so difficult to implement in Rust, since all I want to do is return a computation from a function that happens to be behind a reference. How should I go about implementing Index for a simple structure where the value is calculated on demand?
The Index trait is meant to return a borrowed pointer to a member of self (e.g. an item in a Vec). The signature of the index method from the Index trait makes it impractical to implement it to have the behavior you described, as you'd have to store every value returned by index in self and ensure that the pointers remain valid until the MyStruct is dropped.
This use case does not match the intuition for Index. When I see myStruct[3], my intuition is that, just as for arrays, I'm getting a pointer to some already-initialized data. The interface for Index corroborates this intuition.
I can see two things that you might potentially be trying to achieve:
Getting nice indexing syntax for your datastructure.
In this case I would recommend against the premise of implementing Index and just provide a method that returns a f64 instead of an &f64 like so.
impl MyStruct {
pub fn index(&self, idx: usize) -> f64 {
self.start + (idx as f64) * self.step
}
}
You don't get the operators, which is good because somebody reading [] would be mislead into thinking they were getting a pointer. But you do get the functionality you want. Depending on your use cases you may want to rename this method.
Passing MyStruct to a parameter with Index bounds.
This is trickier with good reason. Index expects the data to be there before it asks for it. You can't generate and return it because index returns f64, and you can't generate it in the datastructure and return a pointer because it doesn't take a &mut self. You'd have to populate these values before the call to index. Some redesign would be in order, and the direction of that redesign would depend on the larger context of your problem.
I want to write an LRU Cache with a memory size limitation rather than the "number of objects" limitation in std. After trying to figure it out for myself, I cheated and looked at an existing implementation, and I almost understand it, but this stops me:
struct KeyRef<K> {
k: *const K,
}
impl<K: Hash> Hash for LruKeyRef<K> {
fn hash<H: Hasher>(&self, state: &mut H) {
unsafe { (*self.k).hash(state) }
}
}
impl<K: PartialEq> PartialEq for LruKeyRef<K> {
fn eq(&self, other: &LruKeyRef<K>) -> bool {
unsafe { (*self.k).eq(&*other.k) }
}
}
It's that last unsafe line that I don't understand. I'm using a HashMap as the underlying structure, the key is stored with the value, and I want the hasher to be able to find it. I make the working hash key a reference to the real key and provide Hash and PartialEq functions such that the HashMap can find and use the key for bucketing purposes. That's easy.
I understand then that I have to compare the two for PartialEq, and so it makes sense to me that I have to use *self.k to dereference the current object, so why &*other.k for the other object? That's what I don't understand. Why isn't it just *other.k? Aren't I just dereferencing both so I can compare the actual keys?
We wish to call PartialEq::eq:
trait PartialEq<Rhs = Self>
where
Rhs: ?Sized,
{
fn eq(&self, other: &Rhs) -> bool;
}
Assuming the default implementation where Rhs = Self and Self = K, we need to end up with two &K types
other.k is of type *const K
*other.k is of type K
&*other.k is of type &K
This much should hopefully make sense.
self.k is of type *const K
*self.k is of type K
The piece that's missing that that method calls are allowed to automatically reference the value they are called on. This is why there's no distinct syntax for a reference and a value, as there would be in C or C++ (foo.bar() vs foo->bar()).
Thus, the K is automatically referenced to get &K, fulfilling the signature.
impl<K: PartialEq> PartialEq for LruKeyRef<K> {
fn eq(&self, other: &LruKeyRef<K>) -> bool {
unsafe { (*self.k).eq(&*other.k) }
}
}
Under typical circumstances, we can call methods taking &self with just a reference to the object. In addition, a chain of references to the object is also implicitly coerced. That is, we can write:
let a: &str = "I'm a static string";
assert_eq!(str.len(), 19);
assert_eq!((&&&&str).len(), 19);
In your case however, we start with a pointer, which must be explicitly dereferenced inside an unsafe scope. Here are the types of all relevant expressions:
self.k : *const K
(*self.k) : K
other.k : *const K
&*other.k : &K
Since equals takes a reference on its right-hand member, we must make it a reference. Unlike in C++, you can not just pass an lvalue as a reference without making this reference-passing explicit, nor can you pass an rvalue to a const reference. You can however, prepend & to a literal in order to obtain a reference to it (foo(&5)). It only appears asymmetrical because (in a way) self.k is the caller and other.k is the callee.
The general setup is I have an array of values I'd like to map() and then chain() with 1 additional value. I've learned from this answer that the proper way to construct that final value is to use std::iter::once. This works and eliminated the below problem, but I would still like to understand it better.
In my broken, likely rust-anti-pattern-riddled example, I was using an array of a single element and then calling into_iter(). This produced a value / reference type-mismatch in the chain.
Question: What is the Rust-idiomatic mechanism for correcting this value / reference mismatch? Particularly if clone and copy are unavailable.
Background: Why is there a type mis-match to begin with?
This much I believe I understand. Based on the definition of std::iter::Map, the item type for the iterator is type Item = B where B is constrained by F: FnMut(<I as Iterator>::Item) -> B (i.e. the mapped type). However array defines the following 2 IntoIterator implementations, both of which appear to produce references.
impl<'a, const N: usize, T> IntoIterator for &'a [T; N] where
[T; N]: LengthAtMost32,
type Item = &'a T
impl<'a, const N: usize, T> IntoIterator for &'a mut [T; N] where
[T; N]: LengthAtMost32,
type Item = &'a mut T
Example demonstrating the issue:
#[derive(PartialEq, Eq, Clone, Copy)]
enum Enum1 {
A, B, C
}
#[derive(PartialEq, Eq, Clone, Copy)]
enum Enum2 {
X, Y, Z
}
struct Data {
// Other data omitted
e1: Enum1,
e2: Enum2
}
struct Consumer {
// Other data omitted
/** Predicate which evaluates if this consumer can consume given Data */
consumes: Box<dyn Fn(&Data) -> bool>
}
fn main() {
// Objective: 3 consumers which consume data with A, B, and X respectively
let v: Vec<Consumer> = [Enum1::A, Enum1::B].iter()
.map(|&e1| Consumer { consumes: Box::new(move |data| data.e1 == e1) })
// This chain results in an iterator type-mismatch:
// expected &Consumer, found Consumer
.chain([Consumer { consumes: Box::new(move |data| data.e2 == Enum2::X) }].into_iter())
.collect(); // Fails as well due to the chain failure
}
Error:
error[E0271]: type mismatch resolving `<std::slice::Iter<'_, Consumer> as std::iter::IntoIterator>::Item == Consumer`
--> src/main.rs:52:10
|
52 | .chain([Consumer { consumes: Box::new(move |data| data.e2 == Enum2::X) }].into_iter())
| ^^^^^ expected reference, found struct `Consumer`
|
= note: expected type `&Consumer`
found type `Consumer`
Rust playground example.
There is a long-standing issue regarding this. The technical details are a bit heavy, but essentially, due to underlying, technical reasons, you cannot take ownership of a fixed-size array and return owned references without a lot of hocus pocus. This becomes obvious when you think about what a fixed-size array is and how it is stored in memory, and how you can get elements out without cloning them.
As a result, due to the implementations you found already, you can only get borrowed references. You can bypass this with arrayvec (as they have a sound implementation of IntoIterator for ArrayVec with owned types), or you can require that all your T: Clone and deal with it that way, at a cost of extra items in memory (temporarily; 90% of the time the compiler optimizes this away).