Rust allows declaring a structure inside a function but it doesn't allow assigning a variable with it in a simple way.
fn f1() -> (something) {
struct mystruct {
x: i32,
}
let s = mystruct;
s
}
fn f2(s: something) {
let obj = s { x: 5 };
println!(obj.x);
}
fn main() {
let s = f1();
f2(s);
}
Is it possible to store a struct into a variable in a different way? How do I write the struct type correctly? In my project, I want to declare a struct inside a function and create instances inside of another one.
How to store a struct into a variable in Rust?
Rust is a statically typed language, and as such it is not possible to store a type into a variable, then use this variable to construct an instance of the type.
This is the reason you are not able to express what the type of s is; there is simply no vocabulary in the language for this.
Depending on what you want to do, you may wish to look into:
Generics: fn f2<T: Default>() would allow creating an instance of any type T implementing the Default trait.
Run-time polymorphism: A factory function FnOnce(i32) -> Box<Trait> could produce an instance of any type implementing Trait from a i32.
Related
Consider some struct (HiddenInaccessibleStruct) that is not accessible, but implements an API trait. The only way to obtain an object of this hidden type is by calling a function, that returns an opaque implementation of this type. Another struct owns some type, that makes use of this API trait. Right now, it seems not possible to assign this field in fn new(). The code below can also be found in rust playgrounds.
// -- public api
trait Bound {
fn call(&self) -> Self;
}
// this is not visible
#[derive(Default)]
struct HiddenInaccessibleStruct;
impl Bound for HiddenInaccessibleStruct {
fn call(&self) -> Self { }
}
// -- public api
pub fn load() -> impl Bound {
HiddenInaccessibleStruct::default()
}
struct Abc<T> where T : Bound {
field : T
}
impl<T> Abc<T> where T : Bound {
pub fn new() -> Self {
let field = load();
Abc {
field // this won't work, since `field` has an opaque type.
}
}
}
Update
The API trait Bound declares a function, that returns Self, hence it is not Sized.
There are two concepts in mid-air collision here: Universal types and existential types. An Abc<T> is a universal type and we, including Abc, can refer to whatever T actually is as T (simple as that). impl Trait-types are Rust's closest approach to existential types, where we only promise that such a type exists, but we can't refer to it (there is no T which holds the solution). This also means your constructor can't actually create a Abc<T>, because it can't decide what T is. Also see this article.
One solution is to kick the problem upstairs: Change the constructor to take a T from the outside, and pass the value into it:
impl<T> Abc<T>
where
T: Bound,
{
pub fn new(field: T) -> Self {
Abc { field }
}
}
fn main() {
let field = load();
let abc = Abc::new(field);
}
See this playground.
This works, but it only shifts the problem: The type of abc in main() is Abc<impl Bound>, which is (currently) impossible to write down. If you change the line to let abc: () = ..., the compiler will complain that you are trying to assign Abc<impl Bound> to (). If you try to comply with the advice and change the line to let abc: Abc<impl Bound> = ..., the compiler will complain that this type is invalid. So you have to leave the type of abc being implied. This brings some useability issues with Abc<impl Bound>, because you can't easily put values of that type into other structs etc.; basically, the existential type "infects" the outer type containing it.
impl Trait-types are mostly useful for immediate consumption, e.g. impl Iterator<Item=...>. In your case, with the aim apparently being to hide the type, you may get away with sealing Bound. In a more general case, it may be better to use dynamic dispatch (Box<dyn Bound>).
Explicitly using String::from in the following code works, but how can I make it automatically use From<OsStringWrap<'a>> trait without explicitly using String::from?
use serde::Serialize; // 1.0.115
struct OsStringWrap<'a>(&'a std::ffi::OsString);
impl<'a> From<OsStringWrap<'a>> for String {
fn from(s: OsStringWrap) -> String {
s.0.to_string_lossy().to_string()
}
}
pub fn insert<T: Serialize + ?Sized, S: Into<String>>(_key: S, _value: &T) {}
fn main() {
for (key, value) in std::env::vars_os() {
// HOW-TO: auto use From<OsStringWrap<'a>> trait
// without explicit `String::from` like below?
/*
insert(OsStringWrap(&key), &OsStringWrap(&value))
*/
// below using `String::from` to make it explicitly
// but want to find a way to make it shorter
insert(OsStringWrap(&key), &String::from(OsStringWrap(&value)))
}
}
Playground, and the insert method is a real case from tera
Your request as currently phrased is impossible. Your insert function accepts a generic type, so nothing exists to tell the compiler what type should be converted to. This tiny example is equivalent:
fn demo<T>(_: T) {}
fn main() {
demo(true.into());
}
Since the compiler can not know what concrete type to pick to convert to, the programmer must specify it.
You could potentially change your function to accept anything that can be turned into a String (e.g. T: Into<String>) or be referenced as a &str (e.g. T: AsRef<str>).
See also:
Type must be known in this context when using Iterator::collect
Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument?
I'm getting started with Rust. I want to have a struct that contains (among other things) a string:
#[derive(Clone, Debug)]
struct Foo {
string_field: &str, // won't compile, but suppose String or Box<str> or &'a str or &'static str...
}
And I want to be able to declare constants or statics of it:
static FOO1: Foo = Foo {
string_field: "",
};
And I also want to be able to have it contain a string constructed at runtime:
let foo2 = Foo {
string_field: ("a".to_owned() + "b").as_str(),
};
I could add a lifetime parameter to Foo so that I can declare that the string reference has the same lifetime. That's fine, except that it then seems to require an explicit lifetime parameter for everything that contains a Foo, which means that it complicates the rest of my program (even parts that don't care about being able to use constant expressions).
I could write
enum StringOfAdequateLifetime {
Static(&'static str),
Dynamic(Box<str>), // or String, if you like
}
struct Foo {
string_field: StringOfAdequateLifetime,
}
and that seems to work so far but clutters up writing out literal Foos.
It seems obvious enough that the desired runtime behavior is sound: when you drop a Foo, drop the string it contains — and if it's static it's never dropped, so no extra information is needed to handle the two cases. Is there a clean way to ask Rust for just that?
(It seems like what I could use is some kind of "smart pointer" type to hold the string that can also be written as a constant expression for the static case, but I haven't seen one in the standard library, and when I tried to genericize StringOfAdequateLifetime to apply to any type, I ran into further complications with implementing and using the various standard traits like Deref, which I suspect were due to something about the differences between Sized and non-Sized types.)
The rust standard library has a built-in type for this exact use case, Cow. It's an enum that can represent either a reference or an owned value, and will clone the value if necessary to allow mutable access. In your particular use case, you could define the struct like so:
struct Foo {
string_field: Cow<'static, str>
}
Then you could instantiate it in one of two ways, depending on whether you want a borrowed constant string or an owned runtime-constructed value:
const BORROWED: Foo = Foo { string_field: Cow::Borrowed("some constant") };
let owned = Foo { string_field: Cow::Owned(String::from("owned string")) };
To simplify this syntax, you can define your own constructor functions for the type using a const fn to allow using the borrowed constructor in a constant context:
impl Foo {
pub const fn new_const(value: &'static str) -> Self {
Self { string_field: Cow::borrowed(value) }
}
pub fn new_runtime(value: String) -> Self {
Self { string_field: Cow::Owned(value) }
}
}
This allows you to use a simpler syntax for initializing the values:
const BORROWED: Foo = Foo::new_const("some constant");
let owned = Foo::new_runtime(String::from("owned string"));
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/
How do I get over something like this:
struct Test {
foo: Option<fn()>
}
impl Test {
fn new(&mut self) {
self.foo = Option::Some(self.a);
}
fn a(&self) { /* can use Test */ }
}
I get this error:
error: attempted to take value of method `a` on type `&mut Test`
--> src/main.rs:7:36
|
7 | self.foo = Option::Some(self.a);
| ^
|
= help: maybe a `()` to call it is missing? If not, try an anonymous function
How do I pass a function pointer from a trait? Similar to what would happen in this case:
impl Test {
fn new(&mut self) {
self.foo = Option::Some(a);
}
}
fn a() { /* can't use Test */ }
What you're trying to do here is get a function pointer from a (to use Python terminology here, since Rust doesn't have a word for this) bound method. You can't.
Firstly, because Rust doesn't have a concept of "bound" methods; that is, you can't refer to a method with the invocant (the thing on the left of the .) already bound in place. If you want to construct a callable which approximates this, you'd use a closure; i.e. || self.a().
However, this still wouldn't work because closures aren't function pointers. There is no "base type" for callable things like in some other languages. Function pointers are a single, specific kind of callable; closures are completely different. Instead, there are traits which (when implemented) make a type callable. They are Fn, FnMut, and FnOnce. Because they are traits, you can't use them as types, and must instead use them from behind some layer of indirection, such as Box<FnOnce()> or &mut FnMut(i32) -> String.
Now, you could change Test to store an Option<Box<Fn()>> instead, but that still wouldn't help. That's because of the other, other problem: you're trying to store a reference to the struct inside of itself. This is not going to work well. If you manage to do this, you effectively render the Test value permanently unusable. More likely is that the compiler just won't let you get that far.
Aside: you can do it, but not without resorting to reference counting and dynamic borrow checking, which is out of scope here.
So the answer to your question as-asked is: you don't.
Let's change the question: instead of trying to crowbar a self-referential closure in, we can instead store a callable that doesn't attempt to capture the invocant at all.
struct Test {
foo: Option<Box<Fn(&Test)>>,
}
impl Test {
fn new() -> Test {
Test {
foo: Option::Some(Box::new(Self::a)),
}
}
fn a(&self) { /* can use Test */ }
fn invoke(&self) {
if let Some(f) = self.foo.as_ref() {
f(self);
}
}
}
fn main() {
let t = Test::new();
t.invoke();
}
The callable being stored is now a function that takes the invocant explicitly, side-stepping the issues with cyclic references. We can use this to store Test::a directly, by referring to it as a free function. Also note that because Test is the implementation type, I can also refer to it as Self.
Aside: I've also corrected your Test::new function. Rust doesn't have constructors, just functions that return values like any other.
If you're confident you will never want to store a closure in foo, you can replace Box<Fn(&Test)> with fn(&Test) instead. This limits you to function pointers, but avoids the extra allocation.
If you haven't already, I strongly urge you to read the Rust Book.
There are few mistakes with your code. new function (by the convention) should not take self reference, since it is expected to create Self type.
But the real issue is, Test::foo expecting a function type fn(), but Test::a's type is fn(&Test) == fn a(&self) if you change the type of foo to fn(&Test) it will work. Also you need to use function name with the trait name instead of self. Instead of assigning to self.a you should assign Test::a.
Here is the working version:
extern crate chrono;
struct Test {
foo: Option<fn(&Test)>
}
impl Test {
fn new() -> Test {
Test {
foo: Some(Test::a)
}
}
fn a(&self) {
println!("a run!");
}
}
fn main() {
let test = Test::new();
test.foo.unwrap()(&test);
}
Also if you gonna assign a field in new() function, and the value must always set, then there is no need to use Option instead it can be like that:
extern crate chrono;
struct Test {
foo: fn(&Test)
}
impl Test {
fn new() -> Test {
Test {
foo: Test::a
}
}
fn a(&self) {
println!("a run!");
}
}
fn main() {
let test = Test::new();
(test.foo)(&test); // Make sure the paranthesis are there
}