I stumbled upon this Rust example in Wikipedia and I am wondering if its possible to convert it to semantically equivalent C++ code?
The program defines a recursive datastructure and implements methods upon it. Recursive datastructures require a layer of indirection, which is provided by a unique pointer, constructed via the box
operator. (These are analogous to the C++ library type std::unique_ptr
, though with more static safety guarantees.)
fn main() {
let list = box Node(1, box Node(2, box Node(3, box Empty)));
println!("Sum of all values in the list: {:i}.", list.multiply_by(2).sum());
}
// `enum` defines a tagged union that may be one of several different kinds
// of values at runtime. The type here will either contain no value, or a
// value and a pointer to another `IntList`.
enum IntList {
Node(int, Box<IntList>),
Empty
}
// An `impl` block allows methods to be defined on a type.
impl IntList {
fn sum(self) -> int {
match self {
Node(value, next) => value + next.sum(),
Empty => 0
}
}
fn multiply_by(self, n: int) -> Box<IntList> {
match self {
Node(value, next) => box Node(value * n, next.multiply_by(n)),
Empty => box Empty
}
}
}
Apparently in C++ version Rusts enum
should be replaced with union
, Rusts Box
should be replaced with std::unique_ptr
and Rusts Node tuple should be std::tuple
type but I just cant wrap my head around how to write equivalent implementation in C++.
I know this is probably not practical (and definitely not the correct way to do things in C++) but I just wanted to see how these languages compare (C++11 features flexible enough for this kind of tinkering?). I would also like to compare compiler generated assembly for semantically equivalent implementations (if even possible).
Disclaimer: I'm not a C++11 expert. Consume with a requisite dose of salt.
As others have commented, there's a few ways of interpreting your question. I'm going to go with an overly aggressive interpretation, since it's the only interesting one:
No, it is not possible to translate that Rust code into equivalent C++ code. Can you translate it into a program that provides the same output? They're both turing complete, so of course you can. Can you translate it so that all semantics in the original are preserved? No.
Most of it can be translated such that it preserves the actual behaviour. Rust-style
enum
s can be replaced bystruct
s with both a tag field and aunion
, along with writing appropriate operator overloads to ensure that you correctly destroy the members of only the variant that's actually stored. You can (presumably) useunique_ptr
in such a way that the memory gets allocated first and then the new value is written directly into the allocation, so there's no copy. I believe you can rewritefn sum(self)
so that it uses an rvaluethis
(although I've never done this, so I could easily be wrong).But the one thing you cannot do in C++, to my knowledge, is replicate linear types. There is no way to statically enforce that a moved value cannot be used again. The best you can do is runtime checks, which must necessarily involve additional overhead. This also plays into why you can't have a non-nullable
unique_ptr
: you wouldn't ever be able to move it, since you have to leave the moved variable in a usable state.Now, that having been said, I should disclaim the previous statement by noting that currently, the Rust compiler emits some runtime checks for dropped (i.e. moved) values, in the form of drop flags. The plan, last I checked, was to remove these runtime checks in favour of purely static destruction, hopefully before 1.0.