Same object with different API faces at compile time? - rust

I have an object that can be in either of two modes: a source or a sink. It is always in one of them and it is always known at compile time (when passed the object you know if you are going to read or write to it obviously).
I can put all the methods on the same object, and just assume I won't be called improperly or error when I do, or I was thinking I could be make two
tuple structs of the single underlying object and attach the methods to those tuple structs instead. The methods are almost entirely disjoint.
It is kind of abusing the fact that both tuple structs have the same layout and there is zero overhead for the casts and tuple storage.
Think of this similar to the Java ByteBuffer and related classes where you write then flip then read then flip back and write more. Except this would catch errors in usage.
However, it does seem a little unusual and might be overly confusing for such a small problem. And it seems like there is a better way to do this -- only requirement is zero overhead so no dynamic dispatch.
https://play.rust-lang.org/?gist=280d2ec2548e4f38e305&version=stable
#[derive(Debug)]
struct Underlying {
a: u32,
b: u32,
}
#[derive(Debug)]
struct FaceA(Underlying);
impl FaceA {
fn make() -> FaceA { FaceA(Underlying{a:1,b:2}) }
fn doa(&self) { println!("FaceA do A {:?}", *self); }
fn dou(&self) { println!("FaceA do U {:?}", *self); }
fn tob(&self) -> &FaceB { unsafe{std::mem::transmute::<&FaceA,&FaceB>(self)} }
}
#[derive(Debug)]
struct FaceB(Underlying);
impl FaceB {
fn dob(&self) { println!("FaceB do B {:?}", *self); }
fn dou(&self) { println!("FaceB do U {:?}", *self); }
fn toa(&self) -> &FaceA { unsafe{std::mem::transmute::<&FaceB,&FaceA>(self)} }
}
fn main() {
let a = FaceA::make();
a.doa();
a.dou();
let b = a.tob();
b.dob();
b.dou();
let aa = b.toa();
aa.doa();
aa.dou();
}

First of all, it seems like you don't understand how ownership works in Rust; you may want to read the Ownership chapter of the Rust Book. Specifically, the way you keep re-aliasing the original FaceA is how you would specifically enable the very thing you say you want to avoid. Also, all the borrows are immutable, so it's not clear how you intend to do any sort of mutation.
As such, I've written a new example from scratch that involves going between two types with disjoint interfaces (view on playpen).
#[derive(Debug)]
pub struct Inner {
pub value: i32,
}
impl Inner {
pub fn new(value: i32) -> Self {
Inner {
value: value,
}
}
}
#[derive(Debug)]
pub struct Upper(Inner);
impl Upper {
pub fn new(inner: Inner) -> Self {
Upper(inner)
}
pub fn into_downer(self) -> Downer {
Downer::new(self.0)
}
pub fn up(&mut self) {
self.0.value += 1;
}
}
#[derive(Debug)]
pub struct Downer(Inner);
impl Downer {
pub fn new(inner: Inner) -> Self {
Downer(inner)
}
pub fn into_upper(self) -> Upper {
Upper::new(self.0)
}
pub fn down(&mut self) {
self.0.value -= 1;
}
}
fn main() {
let mut a = Upper::new(Inner::new(0));
a.up();
let mut b = a.into_downer();
b.down();
b.down();
b.down();
let mut c = b.into_upper();
c.up();
show_i32(c.0.value);
}
#[inline(never)]
fn show_i32(v: i32) {
println!("v: {:?}", v);
}
Here, the into_upper and into_downer methods consume the subject value, preventing anyone from using it afterwards (try accessing a after the call to a.into_downer()).
This should not be particularly inefficient; there is no heap allocation going on here, and Rust is pretty good at moving values around efficiently. If you're curious, this is what the main function compiles down to with optimisations enabled:
mov edi, -1
jmp _ZN8show_i3220h2a10d619fa41d919UdaE
It literally inlines the entire program (save for the show function that I specifically told it not to inline). Unless profiling shows this to be a serious performance problem, I wouldn't worry about it.

Related

How do I get automagic convertions from Rust Into trait?

I have a struct Object for which I have many implementations of From (including isize and &str). I got the sense from this article where Into is described as 'genius' that things could get automatically converted for me. I've incorporated the suggestions that people have made, and made something that is standalone in the playground but it still gets a couple of errors.
#[derive(Copy,Clone)]
pub union Object {
d:f64,
i:isize,
}
impl From<isize> for Object {
fn from(i:isize) -> Self {
Object{i}
}
}
impl From<f64> for Object {
fn from(d:f64) -> Self {
Object{d}
}
}
pub fn old_convert(foo: Object, _elements: &[Object]) -> Object {
foo
}
pub fn new_convert<'a,T>(foo: impl Into<Object>, elements: &'a [T]) -> Object
where
&'a T: Into<Object>,
Object: From<T>,
{
let mut el = Vec::new();
for o in elements.iter() {
el.push(o.into())
}
old_convert(foo.into(),&el)
}
#[test]
fn testOldConvert() {
old_convert(Object::from(42), &[Object::from(3.1415)]);
}
#[test]
fn testNewConvert() {
new_convert(42, &[3.1415]);
}
So you can see what I currently do. I'd like to not have to include all the Object::from(...) when I use my function.
There is 1 error, and 1 problem:
I don't know how to implement the From it's asking for
I don't want to create the temporary vector... surely there's some zero-cost abstraction that allows me to pass the converted array along

Is it possible to create a Box<(T, [U])>?

The question pretty much says it: can I create a boxed tuple (or any struct) that directly contains a slice as one of its fields?
The Vec::into_boxed_slice method works to create Box<[U]>, but I would like to add on some additional data directly in the box alongside the slice.
For example, here is the kind of struct I would like to box up without using Vec into something like Box<(i32,[u8])>:
struct ConsolidateMe {
data: i32,
arr: Vec<u8>,
}
impl ConsolidateMe {
fn new(n: usize) -> Self {
let mut arr = Vec::with_capacity(n);
arr.resize(n, 42);
ConsolidateMe {
data: 15,
arr,
}
}
}
(In my application, I am creating many structs on the heap, each of which contains a small array and a few other values. The arrays have different sizes, but the size doesn't change after the struct is created. I'd like these to be compactly stored and to avoid extra indirection to the extent possible.)
The slice-dst crate looks like a perfect fit.
Example:
use slice_dst::SliceWithHeader;
#[derive(Debug, Clone)]
struct MyType {
inner: Box<SliceWithHeader<String, i32>>,
}
impl MyType {
fn new(value: &str) -> Self {
let values: Vec<i32> = value.split(" ").map(|s| s.trim().parse().unwrap()).collect();
Self {
inner: SliceWithHeader::new(value.into(), values.into_iter()),
}
}
}
fn main() {
println!("{:?}", MyType::new("1 2 3"));
}

How to store struct reference in a vec and use the struct elsewhere later on?

I want to create a struct, store a reference to it in a vec, and then use the struct later on:
pub struct NonTerminal {
name: String
}
impl<'a> NonTerminal {
pub fn new(grammar: &'a mut Grammar, name: &str) -> &'a NonTerminal {
let t = NonTerminal {
name: name.to_string()
};
grammar.non_terminals.push(t);
grammar.non_terminals.last().unwrap()
}
}
pub struct Grammar<'a> {
non_terminals: Vec<NonTerminal>
}
pub fn create_rules<'a>(g: &'a mut grammar::Grammar<'a>) {
let sum = grammar::NonTerminal::new(g, "Sum");
let product = grammar::NonTerminal::new(g, "Product");
// this fails: "cannot borrow `*g` as mutable more than once at a time, second mutable borrow occurs here"
// ...
// use the sum and product vars when constructing rules
}
How to approach this?
You can't do this. This is simply the classical "vector push" example in disguise. Let's take a look at this simplified, but semantically equivalent code:
let mut v = Vec::new();
v.push(27);
let twenty_seven = v.last().unwrap();
v.push(3);
println!("{}", twenty_seven);
This does not compile and will never compile in Rust as this is inherently memory unsafe. You have a reference (a pointer) to an element in the vector. But the call to Vec::push might reallocate, invalidating all references to its elements. In C++ it would compile, but lead to UB because you would attempt to read uninitialized or unallocated memory.
The "answer" to your problem is... not simple. You have to think of another structure for your program. Sometimes, using reference counted smart pointers (like Rc) is useful. And it would easily solve your problem. But in many other situations you are better of completely rethinking your application.
Exactly one &mut borrow of data may occur at any given time.
I'd suggest inverting things so that Grammar has a function to add NonTerminals
pub struct NonTerminal {
name: String
}
impl NonTerminal {
pub fn new(name: &str) -> NonTerminal {
Self {
name: name.to_string()
}
}
}
pub struct Grammar {
pub non_terminals: Vec<NonTerminal>
}
impl Grammar {
fn add_non_terminal(&mut self, s: &str) -> &NonTerminal {
self.non_terminals.push(NonTerminal::new(s));
self.non_terminals.last().unwrap()
}
}
pub fn main() {
let mut g = Grammar {
non_terminals: vec![]
};
let product_ref = g.add_non_terminal("Product");
let sum_ref = g.add_non_terminal("Sum");
}
Updated based on feedback from #Lukas Kalbertodt.
Can iterate over all non-terminals via g.non_terminals

Can I implement a trait which adds information to an external type in Rust?

I just implemented a simple trait to keep the history of a struct property:
fn main() {
let mut weight = Weight::new(2);
weight.set(3);
weight.set(5);
println!("Current weight: {}. History: {:?}", weight.value, weight.history);
}
trait History<T: Copy> {
fn set(&mut self, value: T);
fn history(&self) -> &Vec<T>;
}
impl History<u32> for Weight {
fn set(&mut self, value: u32) {
self.history.push(self.value);
self.value = value;
}
fn history(&self) -> &Vec<u32> {
&self.history
}
}
pub struct Weight {
value: u32,
history: Vec<u32>,
}
impl Weight {
fn new(value: u32) -> Weight {
Weight {
value,
history: Vec::new(),
}
}
}
I don't expect this is possible, but could you add the History trait (or something equivalent) to something which doesn't already have a history property (like u32 or String), effectively tacking on some information about which values the variable has taken?
No. Traits cannot add data members to the existing structures. Actually, only a programmer can do that by modifying the definition of a structure. Wrapper structures or hash-tables are the ways to go.
No, traits can only contain behavior, not data. But you could make a struct.
If you could implement History for u32, you'd have to keep the entire history of every u32 object indefinitely, in case one day someone decided to call .history() on it. (Also, what would happen when you assign one u32 to another? Does its history come with it, or does the new value just get added to the list?)
Instead, you probably want to be able to mark specific u32 objects to keep a history. A wrapper struct, as red75prime's answer suggests, will work:
mod hist {
use std::mem;
pub struct History<T> {
value: T,
history: Vec<T>,
}
impl<T> History<T> {
pub fn new(value: T) -> Self {
History {
value,
history: Vec::new(),
}
}
pub fn set(&mut self, value: T) {
self.history.push(mem::replace(&mut self.value, value));
}
pub fn get(&self) -> T
where
T: Copy,
{
self.value
}
pub fn history(&self) -> &[T] {
&self.history
}
}
}
It's generic, so you can have a History<u32> or History<String> or whatever you want, but the get() method will only be implemented when the wrapped type is Copy.* Your Weight type could just be an alias for History<u32>. Here it is in the playground.
Wrapping this code in a module is a necessary part of maintaining the abstraction. That means you can't write weight.value, you have to call weight.get(). If value were marked pub, you could assign directly to weight.value (bypassing set) and then history would be inaccurate.
As a side note, you almost never want &Vec<T> when you can use &[T], so I changed the signature of history(). Another thing you might consider is returning an iterator over the previous values (perhaps in reverse order) instead of a slice.
* A better way of getting the T out of a History<T> is to implement Deref and write *foo instead of foo.get().

Using the Rust compiler to prevent forgetting to call a method

I have some code like this:
foo.move_right_by(10);
//do some stuff
foo.move_left_by(10);
It's really important that I perform both of those operations eventually, but I often forget to do the second one after the first. It causes a lot of bugs and I'm wondering if there is an idiomatic Rust way to avoid this problem. Is there a way to get the rust compiler to let me know when I forget?
My idea was to maybe somehow have something like this:
// must_use will prevent us from forgetting this if it is returned by a function
#[must_use]
pub struct MustGoLeft {
steps: usize;
}
impl MustGoLeft {
fn move(&self, foo: &mut Foo) {
foo.move_left_by(self.steps);
}
}
// If we don't use left, we'll get a warning about an unused variable
let left = foo.move_left_by(10);
// Downside: move() can be called multiple times which is still a bug
// Downside: left is still available after this call, it would be nice if it could be dropped when move is called
left.move();
Is there a better way to accomplish this?
Another idea is to implement Drop and panic! if the struct is dropped without having called that method. This isn't as good though because it's a runtime check and that is highly undesirable.
Edit: I realized my example may have been too simple. The logic involved can get quite complex. For example, we have something like this:
foo.move_right_by(10);
foo.open_box(); // like a cardboard box, nothing to do with Box<T>
foo.move_left_by(10);
// do more stuff...
foo.close_box();
Notice how the operations aren't performed in a nice, properly nested order. The only thing that's important is that the inverse operation is always called afterwards. The order sometimes needs to be specified in a certain way in order to make the code work as expected.
We can even have something like this:
foo.move_right_by(10);
foo.open_box(); // like a cardboard box, nothing to do with Box<T>
foo.move_left_by(10);
// do more stuff...
foo.move_right_by(10);
foo.close_box();
foo.move_left_by(10);
// do more stuff...
You can use phantom types to carry around additional information, which can be used for type checking without any runtime cost. A limitation is that move_left_by and move_right_by must return a new owned object because they need to change the type, but often this won't be a problem.
Additionally, the compiler will complain if you don't actually use the types in your struct, so you have to add fields that use them. Rust's std provides the zero-sized PhantomData type as a convenience for this purpose.
Your constraint could be encoded like this:
use std::marker::PhantomData;
pub struct GoneLeft;
pub struct GoneRight;
pub type Completed = (GoneLeft, GoneRight);
pub struct Thing<S = ((), ())> {
pub position: i32,
phantom: PhantomData<S>,
}
// private to control how Thing can be constructed
fn new_thing<S>(position: i32) -> Thing<S> {
Thing {
position: position,
phantom: PhantomData,
}
}
impl Thing {
pub fn new() -> Thing {
new_thing(0)
}
}
impl<L, R> Thing<(L, R)> {
pub fn move_left_by(self, by: i32) -> Thing<(GoneLeft, R)> {
new_thing(self.position - by)
}
pub fn move_right_by(self, by: i32) -> Thing<(L, GoneRight)> {
new_thing(self.position + by)
}
}
You can use it like this:
// This function can only be called if both move_right_by and move_left_by
// have been called on Thing already
fn do_something(thing: &Thing<Completed>) {
println!("It's gone both ways: {:?}", thing.position);
}
fn main() {
let thing = Thing::new()
.move_right_by(4)
.move_left_by(1);
do_something(&thing);
}
And if you miss one of the required methods,
fn main(){
let thing = Thing::new()
.move_right_by(3);
do_something(&thing);
}
then you'll get a compile error:
error[E0308]: mismatched types
--> <anon>:49:18
|
49 | do_something(&thing);
| ^^^^^^ expected struct `GoneLeft`, found ()
|
= note: expected type `&Thing<GoneLeft, GoneRight>`
= note: found type `&Thing<(), GoneRight>`
I don't think #[must_use] is really what you want in this case. Here's two different approaches to solving your problem. The first one is to just wrap up what you need to do in a closure, and abstract away the direct calls:
#[derive(Debug)]
pub struct Foo {
x: isize,
y: isize,
}
impl Foo {
pub fn new(x: isize, y: isize) -> Foo {
Foo { x: x, y: y }
}
fn move_left_by(&mut self, steps: isize) {
self.x -= steps;
}
fn move_right_by(&mut self, steps: isize) {
self.x += steps;
}
pub fn do_while_right<F>(&mut self, steps: isize, f: F)
where F: FnOnce(&mut Self)
{
self.move_right_by(steps);
f(self);
self.move_left_by(steps);
}
}
fn main() {
let mut x = Foo::new(0, 0);
println!("{:?}", x);
x.do_while_right(10, |foo| {
println!("{:?}", foo);
});
println!("{:?}", x);
}
The second approach is to create a wrapper type which calls the function when dropped (similar to how Mutex::lock produces a MutexGuard which unlocks the Mutex when dropped):
#[derive(Debug)]
pub struct Foo {
x: isize,
y: isize,
}
impl Foo {
fn new(x: isize, y: isize) -> Foo {
Foo { x: x, y: y }
}
fn move_left_by(&mut self, steps: isize) {
self.x -= steps;
}
fn move_right_by(&mut self, steps: isize) {
self.x += steps;
}
pub fn returning_move_right(&mut self, x: isize) -> MovedFoo {
self.move_right_by(x);
MovedFoo {
inner: self,
move_x: x,
move_y: 0,
}
}
}
#[derive(Debug)]
pub struct MovedFoo<'a> {
inner: &'a mut Foo,
move_x: isize,
move_y: isize,
}
impl<'a> Drop for MovedFoo<'a> {
fn drop(&mut self) {
self.inner.move_left_by(self.move_x);
}
}
fn main() {
let mut x = Foo::new(0, 0);
println!("{:?}", x);
{
let wrapped = x.returning_move_right(5);
println!("{:?}", wrapped);
}
println!("{:?}", x);
}
I only looked at the initial description and probably missed the details in the conversation but one way to enforce the actions is to consume the original object (going right) and replace it with one that forces you to to move left by same amount before you can do whatever you wanted to do to finish the task.
The new type can forbid / require different calls to be made before getting to a finished state. For example (untested):
struct CanGoRight { .. }
impl CanGoRight {
fn move_right_by(self, steps: usize) -> MustGoLeft {
// Note: self is consumed and only `MustGoLeft` methods are allowed
MustGoLeft{steps: steps}
}
}
struct MustGoLeft {
steps: usize;
}
impl MustGoLeft {
fn move_left_by(self, steps: usize) -> Result<CanGoRight, MustGoLeft> {
// Totally making this up as I go here...
// If you haven't moved left at least the same amount of steps,
// you must move a bit further to the left; otherwise you must
// switch back to `CanGoRight` again
if steps < self.steps {
Err(MustGoLeft{ steps: self.steps - steps })
} else {
Ok(CanGoRight{ steps: steps - self.steps })
}
}
fn open_box(self) -> MustGoLeftCanCloseBox {..}
}
let foo = foo.move_right_by(10); // can't move right anymore
At this point foo can no longer move right as it isn't allowed by MustGoLeft but it can move left or open the box. If it moves left far enough it gets back to the CanGoRight state again. But if it opens the box then totally new rules apply. Either way you'll have to deal with both possibilities.
There's probably going to be some duplication between the states, but should be easy enough to refactor. Adding a custom trait might help.
In the end it sounds like you're making a state machine of sorts. Maybe https://hoverbear.org/2016/10/12/rust-state-machine-pattern/ will be of use.

Resources