When to use impl on an empty struct [closed] - rust

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 12 months ago.
Improve this question
I saw the following code on the internet:
pub struct Processor;
impl Processor {
pub fn process(
) -> i32 {
// some stuff here
}
}
and used as:
let a = Processor::process();
What's the advantage of having struct here at all? Can the same thing somehow be achieved without it?

Here are a couple of the more common examples you might run into. I'm sure there are others I forgot, but these will hopefully help you when reading rust code in the future.
Creating structs to hold different implementations of a trait
You may find yourself in a situation where it would be nice to be able to use a custom handler, but want to avoid the overhead of storing a function inside of each struct. An easy alternative is to create a trait for it instead and define types for the sole purpose of having different versions of a trait implemented on them.
This way you can use them like compile-time type modifiers which allow for core functionality to be easily swapped or redefined later without any extra overhead or requiring extra information be stored in a struct.
trait Smoothing {
fn smooth(a: i32, b: i32) -> i32;
}
struct LinearStrategy;
impl Smoothing for LinearStrategy {
fn smooth(a: i32, b: i32) -> i32 {
(a + b) / 2
}
}
struct GeometricStrategy;
impl Smoothing for GeometricStrategy {
fn smooth(a: i32, b: i32) -> i32 {
i32::sqrt(a * a + b * b)
}
}
struct ComplexStruct<T> {
/* etc. */
_phantom: PhantomData<T>,
}
// Change how ComplexStruct operates at compile time
impl<T: Smoothing> ComplexStruct<T> {
pub fn sample(&self, x: i32) -> i32 {
T::smooth(self.raw_sample(x - 1), self.raw_sample(x + 1))
}
}
The best example I could find of this is probably Vec. It may not seem like it at first, but Vec has 2 type parameters. In Vec<T, A = Global>, the A is the allocator used by Vec. By default it is set to the global allocator, but in some cases it can be really handy to be able to easily switch it out with something else and still have access to all of the normal functionality of Vec.
Placeholders for FFI
When creating a rust api for a C/other library it may make sense to add types to mirror the C api without containing the same data. Normally that will end up looking like this where a pointer is wrapped with a safe rust alternative and a placeholder lifetime (Since rust does not own this data).
pub struct Foo<'a> {
ptr: *mut sys::Foo,
_phantom: PhantomData<&'a ()>,
}
However, it may also make sense to use a struct in cases where you need to enforce constructors/destructors are called for a resource. Since Rust calls a struct's drop function when it falls out of scope then gets dropped from memory, it is quite easy to enforce these rules. In this case, by having a struct we can enforce some pre-requisite to be filled to gain access to the functions in the struct.
pub struct FooApi;
impl FooApi {
pub fn new() -> Self {
unsafe { sys::init_thread_foo(); }
FooApi
}
/// Some call that is only safe if sys::init_thread_foo() has been called
pub fn do_something(&self) { /* ... */}
}
/// Call FFI function to dispose of this resource once this FooApi is no longer needed
impl Drop for FooApi {
fn drop(&mut self) {
unsafe {
sys::dispose_thread_foo();
}
}
}
Verbosity
Sometimes a struct could probably be replaced with a module. However depending on the developer, they may prefer to use a struct instead in cases where it makes more conceptual sense to think of acting upon an object. Often these cases are reasonably rare and usually indicate that a type previously or will in the future be split into traits and generified. Alternatively it may also be used in cases where there are a couple equivalent, but non-identical alternatives that can be swapped between (Ex: types of CPUs ).
So far those are a couple of the causes that come to mind, but I may come back to add more.

Related

How to offer an API that stores values of different types and can return them with the original type restored?

I want to offer a safe API like below FooManager. It should be able to store arbitrary user-defined values that implement a trait Foo. It should also be able to hand them back later - not as trait object (Box<dyn Foo>) but as the original type (Box<T> where T: Foo). At least conceptually it should be possible to offer this as a safe API, by using generic handles (Handle<T>), see below.
Additional criteria:
The solution should work in stable Rust (internal usage of unsafe blocks is perfectly okay though).
I don't want to modify the trait Foo, as e.g. suggested in How to get a reference to a concrete type from a trait object?. It should work without adding a method as_any(). Reasoning: Foo shouldn't have any knowledge about the fact that it might be stored in containers and be restored to the actual type.
trait Foo {}
struct Handle<T> {
// ...
}
struct FooManager {
// ...
}
impl FooManager {
// A real-world API would complain if the value is already stored.
pub fn keep_foo<T: Foo>(&mut self, foo: Box<T>) -> Handle<T> {
// ...
}
// In a real-world API this would return an `Option`.
pub fn return_foo<T: Foo>(&mut self, handle: Handle<T>) -> Box<T> {
// ...
}
}
I came up with this (Rust Playground) but not sure if there's a better way or if it's safe even. What do you think of that approach?

Use Trait as Vec Type

I'm new to Rust and have seen some examples of people using Box to allow pushing many types that implement a certain Trait onto a Vec. When using a Trait with Generics, I have run into an issue.
error[E0038]: the trait `collision::collision_detection::Collidable` cannot be made into an object
--> src/collision/collision_detection.rs:19:5
|
19 | collidables: Vec<Box<Collidable<P, M>>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `collision::collision_detection::Collidable` cannot be made into an object
|
= note: method `get_ncollide_shape` has generic type parameters
error: aborting due to previous error
error: Could not compile `game_proto`.
To learn more, run the command again with --verbose.
Here is my code
extern crate ncollide;
extern crate nalgebra as na;
use self::ncollide::shape::Shape;
use self::ncollide::math::Point;
use self::ncollide::math::Isometry;
use self::na::Isometry2;
pub trait Collidable<P: Point, M> {
fn get_ncollide_shape<T: Shape<P, M>>(&self) -> Box<T>;
fn get_isometry(&self) -> Isometry2<f64>;
}
pub struct CollisionRegistry<P, M>
where
P: Point,
M: Isometry<P>,
{
collidables: Vec<Box<Collidable<P, M>>>,
}
impl<P: Point, M: Isometry<P>> CollisionRegistry<P, M> {
pub fn new() -> Self {
let objs: Vec<Box<Collidable<P, M>>> = Vec::new();
CollisionRegistry { collidables: objs }
}
pub fn register<D>(&mut self, obj: Box<D>)
where
D: Collidable<P, M>,
{
self.collidables.push(obj);
}
}
I'm trying to use collidables as a list of heterogenous game objects that will give me ncollide compatible Shapes back to feed into the collision detection engine.
EDIT:
To clear up some confusion. I'm not trying to construct and return an instance of a Trait. I'm just trying to create a Vec that will allow any instance of the Collidable trait to be pushed onto it.
Rust is a compiled language, so when it compiles your code, it needs to know all of the information it might need to generate machine code.
When you say
trait MyTrait {
fn do_thing() -> Box<u32>;
}
struct Foo {
field: Box<MyTrait>
}
you are telling Rust that Foo will contain a box containing anything implementing MyTrait. By boxing the type, the compiler will erase any additional data about the data type that isn't covered by the trait. These trait objects are implemented as a set of data fields and a table of functions (called a vtable) that contains the functions exposed by the trait, so they can be called.
When you change
fn do_thing() -> Box<u32>;
to
fn do_thing<T>() -> Box<T>;
it may look similar, but the behavior is much different. Let's take a normal function example
fn do_thing<T>(val: T) { }
fn main() {
do_thing(true);
do_thing(45 as u32);
}
the compiler performs what is a called monomorphization, which means your code in the compiler becomes essentially
fn do_thing_bool(val: bool) { }
fn do_thing_num(val: u32) { }
fn main() {
do_thing_bool(true);
do_thing_num(45 as u32);
}
The key thing to realize is that you are asking it to do the same thing for your trait. The problem is that the compiler can't do it. The example above relies on knowing ahead of time that do_thing is called with a number in one case and a boolean in another, and it can know with 100% certainty that those are the only two ways the function is used.
With your code
trait MyTrait {
fn do_thing<T>() -> Box<T>;
}
the compiler does not know what types do_thing will be called with, so it has no way to generate functions you'd need to call. To do that, wherever you convert the struct implementing Collidable into a boxed object it would have to know every possible return type get_ncollide_shape could have, and that is not supported.
Other links for this:
Understanding Traits and Object Safety
https://www.reddit.com/r/rust/comments/3an132/how_to_wrap_a_trait_object_that_has_generic/

Why doesn't String implement From<&String>?

Background
I know that in Rust people prefer &str rather than &String. But in some case we were only given &String.
One example is when you call std::iter::Iterator::peekable. The return value is a Peekable<I> object that wraps the original iterator into it and gives you one extra method peek.
The point here is that peek only gives you a reference to the iterator item. So if you have an iterator that contains Strings, you only have &String in this case. Of cause, you can easily use as_str to get a &str but in the code I will show below it is equivalent to a call to clone.
The question
This code
#[derive(Debug)]
struct MyStruct(String);
impl MyStruct {
fn new<T>(t: T) -> MyStruct
where
T: Into<String>,
{
MyStruct(t.into())
}
}
fn main() {
let s: String = "Hello world!".into();
let st: MyStruct = MyStruct::new(&s);
println!("{:?}", st);
}
doesn't compile because String doesn't implement From<&String>. This is not intuitive.
Why does this not work? Is it just a missing feature of the standard library or there are some other reasons that prevent the standard library from implementing it?
In the real code, I only have a reference to a String and I know to make it work I only need to call clone instead, but I want to know why.
To solve your problem, one could imagine adding a new generic impl to the standard library:
impl<'a, T: Clone> From<&'a T> for T { ... }
Or to make it more generic:
impl<B, O> From<B> for O where B: ToOwned<Owned=O> { ... }
However, there are two problems with doing that:
Specialization: the specialization feature that allows to overlapping trait-impls is still unstable. It turns out that designing specialization in a sound way is way more difficult than expected (mostly due to lifetimes).
Without it being stable, the Rust devs are very careful not to expose that feature somewhere in the standard library's public API. This doesn't mean that it isn't used at all in std! A famous example is the specialized ToString impl for str. It was introduced in this PR. As you can read in the PR's discussion, they only accepted it because it does not change the API (to_string() was already implemented for str).
However, it's different when we would add the generic impl above: it would change the API. Thus, it's not allowed in std yet.
core vs std: the traits From and Into are defined in the
core library, whereas Clone and ToOwned are defined in std. This means that we can't add a generic impl in core, because core doesn't know anything about std. But we also can't add the generic impl in std, because generic impls need to be in the same crate as the trait (it's a consequence of the orphan rules).
Thus, it would required some form of refactoring and moving around definitions (which may or may not be difficult) before able to add such a generic impl.
Note that adding
impl<'a> From<&'a String> for String { ... }
... works just fine. It doesn't require specialization and doesn't have problems with orphan rules. But of course, we wouldn't want to add a specific impl, when the generic impl would make sense.
(thanks to the lovely people on IRC for explaining stuff to me)
Since String does implement From<&str>, you can make a simple change:
fn main() {
let s: String = "Hello world!".into();
// Replace &s with s.as_str()
let st: MyStruct = MyStruct::new(s.as_str());
println!("{:?}", st);
}
All &Strings can be trivially converted into &str via as_str, which is why all APIs should prefer to use &str; it's a strict superset of accepting &String.

Is it possible to #[derive] traits on crate-included structs? [duplicate]

This question already has answers here:
How do I implement a trait I don't own for a type I don't own?
(3 answers)
Closed 7 years ago.
I want to provide an implementation of a trait ToHex (not defined by me, from serialize) for a primitive type u8:
impl ToHex for u8 {
fn to_hex(&self) -> String {
self.to_str_radix(16)
}
}
The problem is I get this compiler error:
error: cannot provide an extension implementation where both trait and type are not defined in this crate
I understand the reason of this error and its logic, this is because both the trait and the primitive type are external to my code. But how can I handle this situation and provide an ToHex implementation for u8? And more generally how do you handle this kind of issue, it seems to me that this problem must be common and it should be possible and easy to extend types like this?
You should use a newtype struct to do this:
pub struct U8(pub u8)
impl ToHex for U8 {
fn to_hex(&self) -> String {
let U8(x) = *self;
x.to_str_radix(16)
}
}
This does mean, however, that you should wrap u8 into U8 where you need to perform this conversion:
let x: u8 = 127u8
// println!("{}", x.to_hex()); // does not compile
println!("{}", U8(x).to_hex());
This is absolutely free in terms of performance.
I realize this is almost a year old, but the answer was never accepted and I think I've found an alternate solution, that I thought would be good to document here.
In order to extend the functionality of the u8 through traits, instead of trying to extend ToHex, why not create a new trait?
trait MyToHex {
fn to_hex(&self) -> String;
}
impl MyToHex for u8 {
fn to_hex(&self) -> String {
format!("{:x}", *self)
}
}
then used like so
fn main() {
println!("{}", (16).to_hex());
}
This has the advantage that you don't have to wrap every u8 variable with a new and superfluous data type.
The disadvantage is that you still can't use a u8 in a external function (i.e std library, or one you have no control over) that requires the ToHex trait (Vladimir Matveev's solution works in this case), but from OP it sounds like all you want to do is extend u8 only inside your code.

How to make a struct where one of the fields refers to another field

I have the following problem: I have a have a data structure that is parsed from a buffer and contains some references into this buffer, so the parsing function looks something like
fn parse_bar<'a>(buf: &'a [u8]) -> Bar<'a>
So far, so good. However, to avoid certain lifetime issues I'd like to put the data structure and the underlying buffer into a struct as follows:
struct BarWithBuf<'a> {bar: Bar<'a>, buf: Box<[u8]>}
// not even sure if these lifetime annotations here make sense,
// but it won't compile unless I add some lifetime to Bar
However, now I don't know how to actually construct a BarWithBuf value.
fn make_bar_with_buf<'a>(buf: Box<[u8]>) -> BarWithBuf<'a> {
let my_bar = parse_bar(&*buf);
BarWithBuf {buf: buf, bar: my_bar}
}
doesn't work, since buf is moved in the construction of the BarWithBuf value, but we borrowed it for parsing.
I feel like it should be possible to do something along the lines of
fn make_bar_with_buf<'a>(buf: Box<[u8]>) -> BarWithBuf<'a> {
let mut bwb = BarWithBuf {buf: buf};
bwb.bar = parse_bar(&*bwb.buf);
bwb
}
to avoid moving the buffer after parsing the Bar, but I can't do that because the whole BarWithBuf struct has to be initalised in one go.
Now I suspect that I could use unsafe code to partially construct the struct, but I'd rather not do that.
What would be the best way to solve this problem? Do I need unsafe code? If I do, would it be safe do to this here? Or am I completely on the wrong track here and there is a better way to tie a data structure and its underlying buffer together?
I think you're right in that it's not possible to do this without unsafe code. I would consider the following two options:
Change the reference in Bar to an index. The contents of the box won't be protected by a borrow, so the index might become invalid if you're not careful. However, an index might convey the meaning of the reference in a clearer way.
Move Box<[u8]> into Bar, and add a function buf() -> &[u8] to the implementation of Bar; instead of references, store indices in Bar. Now Bar is the owner of the buffer, so it can control its modification and keep the indices valid (thereby avoiding the problem of option #1).
As per DK's suggestion below, store indices in BarWithBuf (or in a helper struct BarInternal) and add a function fn bar(&self) -> Bar to the implementation of BarWithBuf, which constructs a Bar on-the-fly.
Which of these options is the most appropriate one depends on the actual problem context. I agree that some form of "member-by-member construction" of structs would be immensely helpful in Rust.
Here's an approach that will work through a little bit of unsafe code. This approach requires that you are okay with putting the referred-to thing (here, your [u8]) on the heap, so it won't work for direct reference of a sibling field.
Let's start with a toy Bar<'a> implementation:
struct Bar<'a> {
refs: Vec<&'a [u8]>,
}
impl<'a> Bar<'a> {
pub fn parse(src: &'a [u8]) -> Self {
// placeholder for actually parsing goes here
Self { refs: vec![src] }
}
}
We'll make BarWithBuf that uses a Bar<'static>, as 'static is the only lifetime with an an accessible name. The buffer we store things in can be anything that doesn't move the target data around on us. I'm going to go with a Vec<u8>, but Box, Pin, whatever will work fine.
struct BarWithBuf {
buf: Vec<u8>,
bar: Bar<'static>,
}
The implementation requires a tiny bit of unsafe code.
impl BarWithBuf {
pub fn new(buf: Vec<u8>) -> Self {
// The `&'static [u8]` is inferred, but writing it here for demo
let buf_slice: &'static [u8] = unsafe {
// Going through a pointer is a "good" way to get around lifetime checks
std::slice::from_raw_parts(&buf[0], buf.len())
};
let bar = Bar::parse(buf_slice);
Self { buf, bar }
}
/// Access to Bar should always come through this function.
pub fn bar(&self) -> &Bar {
&self.bar
}
}
The BarWithBuf::bar is an important function to re-associate the proper lifetimes to the references. Rust's lifetime elision rules make the function equivalent to pub fn bar<'a>(&'a self) -> &'a Bar<'a>, which turns out to be exactly what we want. The lifetime of the slices in BarWithBuf::bar::refs are tied to the lifetime of BarWithBuf.
WARNING: You have to be very careful with your implementation here. You cannot make #[derive(Clone)] for BarWithBuf, since the default clone implementation will clone buf, but the elements of bar.refs will still point to the original. It is only one line of unsafe code, but the safety is still off in the "safe" bits.
For larger bits of self-referencing structures, there's the ouroboros crate, which wraps up a lot of unsafe bits for you. The techniques are similar to the one I described above, but they live behind macros, which is a more pleasant experience if you find yourself making a number of self-references.

Resources