How do I access "private" struct's field from other files in Rust? - struct

As advised by my lead programmer, who's not knowledgeable in Rust but knows a lot about OO languages like Java, C#, and such, I should separate the functions and associated methods of a particular "class," in Rust it's more or less a struct, from its model or definition to another file. But I had trouble accessing a struct's field/data member from another file. It feels icky to just attach pub before every struct field's name.
// some_model.rs
// This feels icky
pub struct SomeStruct {
pub name: String,
pub id: u32,
}
Just so other files could access the aforementioned struct above
// some_adapter.rs
impl SomeStruct {
pub fn get_id(&self) -> u32 {
self.id
}
pub fn get_name(&self) -> &'static str {
self.name
}
pub fn new(name: &'static str, id: u32) -> Self {
SomeModel {
name,
id
}
}
}
So how does one access such fields from a different file?

Warning
Based from the comments I've received, I'll put this at the top
THIS IS AN UN-RUSTY SOLUTION
If your lead programmer says you should do the following, or do what's indicate in the question, protest and show them this post.
Proposed Answer
I've scoured the net for some answers, every link is a piece of the whole puzzle. See the references below the post. And eventually, I think I found an apt solution, without using the much warned include! macro.
I used the following syntax profusely:
Syntax
Visibility :
pub
| pub ( crate )
| pub ( self )
| pub ( super )
| pub ( in SimplePath )
At first glance it was confusing for me, and for the others like me who might be perplexed as well, I had posted my findings with a sample code.
The directory, using broot
src
├──dal
│ ├──adapter
│ │ └──some_adapter.rs
│ ├──adapter.rs
│ ├──model
│ │ └──some_model.rs
│ └──model.rs
├──dal.rs
├──lib.rs
└──main.rs
I intended to include numerous sub-directories as well, as I reckoned it might add incidental information to those likely having the same knowledge as I in Rust.
I executed cargo new visibility_sample
dal/model/some_model.rs
// dal/model/some_model.rs
pub struct SomeModel {
// take note of the following syntax
pub(in super::super) name: &'static str,
pub(in super::super) id: u32,
}
dal/adapter/some_adapter.rs
// dal/adapter/some_adapter.rs
use super::super::model::some_model::SomeModel;
impl SomeModel {
pub fn get_id(&self) -> u32 {
self.id
}
pub fn get_name(&self) -> &'static str {
self.name
}
pub fn new(name: &'static str, id: u32) -> Self {
SomeModel {
name,
id
}
}
}
dal/model.rs
// dal/model.rs
pub mod some_model;
dal/adapter.rs
// dal/adapter.rs
pub mod some_adapter;
dal.rs
// dal.rs
pub mod model;
pub mod adapter;
lib.rs
// lib.rs
pub mod dal;
And finally main.rs
// main.rs
use visibility_sample::dal::model::some_model::SomeModel;
fn main() {
let object = SomeModel::new("Mike", 3);
println!("name: {}, id: {}", object.get_name(), object.get_id());
}
running cargo run
Compiling visibility_sample v0.1.0 (C:\Users\miked\git\visibility_sample)
Finished dev [unoptimized + debuginfo] target(s) in 1.40s
Running `target\debug\visibility_sample.exe`
name: Mike, id: 3
but if main.rs has the following code:
// main.rs
use visibility_sample::dal::model::some_model::SomeModel;
fn main() {
let object = SomeModel::new("Mike", 3);
println!("name: {}, id: {}", object.name, object.id);
}
Powershell prints out Rust compiler's beautiful and informative error printing:
error[E0616]: field `name` of struct `SomeModel` is private
--> src\main.rs:5:41
|
5 | println!("name: {}, id: {}", object.name, object.id);
| ^^^^ private field
error[E0616]: field `id` of struct `SomeModel` is private
--> src\main.rs:5:54
|
5 | println!("name: {}, id: {}", object.name, object.id);
| ^^ private field
error: aborting due to 2 previous errors
I placed "" quotation marks in the title for "private" because I'm not certain whether that word is still applicable with the term I'm trying to convey with my posted solution.
References
https://doc.rust-lang.org/reference/visibility-and-privacy.html
https://users.rust-lang.org/t/implement-private-struct-in-different-files/29407
Move struct into a separate file without splitting into a separate module?
Proper way to use rust structs from other mods
P.S.
Perhaps to those gifted and keen, they can easily understand the solution with a few or even a single link in my references on their own, but to someone like me, I have to read these links a whole lot and repeatedly get back to it for tinkering on my own.

Related

Module resolution

I have the following code in src/
main.rs
a.rs
b.rs
Here's the code:
main.rs
mod a;
mod b;
use crate::a::Summary;
use crate::b::Person;
fn main() {
let p = Person{ first: "John".to_string(), last: "Doe".to_string() } ;
sum(p) ;
}
fn sum(summary: impl Summary) {
println!("{}", summary.summarize()) ;
}
a.rs
pub trait Summary {
fn summarize(&self) -> String ;
}
b.rs
use crate::Summary;
pub struct Person {
pub first: String,
pub last: String,
}
impl Summary for Person {
fn summarize(&self) -> String {
format!("{}, {}.", self.last, self.first)
}
}
What I don't understand is how does "use crate::Summary;" not cause a problem in b.rs? It should be "use crate::a::Summary;" or even "use super::a::Summary;", but for some reason use crate::Summary works. Is there some kind of funky search logic being applied here under the hood?
Items defined without a visibility specifier are available to the module that they're defined in and all of its sub-modules.
Since a and b are submodules of the crate root module, they can access the Summary object that was imported via a use declaration in main into the crate root module.

Why are some clippy lints gone if my modules are public?

I have a code that looks approximately like this:
// src/bitboard.rs
#[derive(Copy, Clone, Debug)]
pub struct Bitboard {
value: u64
}
impl Bitboard {
pub const fn new(value: u64) -> Self {
Self { value }
}
pub const fn get_bit(&self, k: u64) -> u64 {
((1u64 << k) & self.value) >> k
}
}
// src/main.rs
pub mod bitboard;
use crate::bitboard::Bitboard;
fn main() {
let bitboard = Bitboard::new(0);
dbg!(bitboard);
}
If I compile it exactly like this, it works without any errors or warnings.
However, if I change my pub mod bitboard to mod bitboard, then clippy starts giving me this warning:
warning: this argument (8 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
pub const fn get_bit(&self, k: u64) -> u64 {
^^^^^ help: consider passing by value instead: `self`
= note: `-W clippy::trivially-copy-pass-by-ref` implied by `-W clippy::pedantic`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
I understand what clippy is telling me to do. But I do not understand why it does not suggest this when my module is declared as public. Any ideas?
P.S.: I am using rustc 1.60.0, clippy 0.1.60.
Edit: I should also add that this is not the only extra lint that happens when I remove the pub from my modules. For instance, I also had 1 new clippy::upper_case_acronyms, and 1 new clippy::enum_variant_names.
Edit 2: As requested, I'm including more examples to show the same behaviour happening to clippy::upper-case_acronyms and clippy::enum_variant_names:
// src/fen.rs
pub struct FEN; // upper case acronyms happens here
pub enum FENValidationError { // enum variant names happens here
InvalidFieldsCount,
InvalidPiecePlacement,
InvalidRankSize,
}
// src/main.rs
mod fen; // same thing here. If this becomes `pub mod fen`, the two lint warnings above disappear.
Because changing a public API like that is a breaking change.
Changing the argument to pass by value like clippy recommends is easy if its an internal method, since you have control over all of the code that uses it. But if the argument is on a function or method exposed as part of the public API, changing it would require that all the other projects that use it change too, which is usually unacceptable for something as minor as passing a small Copy value by reference.

Include main.rs in other module

I'm new to rust. I know, for calling a module in same folder I need to write mod <module name> for other folder mod <module name>{ include!("path to module") }. I want to include main.rs in extra.rs, present in same folder, so that I can use Summary trait for structure feed in extra.rs. I get error recursion limit reached while expanding the macro 'include'.
How I can include main.rs in extra.rs? Is there a better way to write the same code?
error
error: recursion limit reached while expanding the macro `include`
--> src/extra.rs:3:5
|
3 | include!("main.rs");
| ^^^^^^^^^^^^^^^^^^^^
|
= help: consider adding a `#![recursion_limit="256"]` attribute to your crate
error: aborting due to previous error
error: could not compile `office_manager`.
main.rs
mod extra;
pub trait Summary {
fn print_summry(&self) -> String;
}
pub struct Tweet {
name: String,
message: String
}
impl Summary for Tweet {
fn print_summry(&self) -> String {
format!("{}: {}",self.name,self.message)
}
}
fn main() {
let t = extra::Feed {
name: String::from("Hanuman"),
message: String::from("Jai sri Ram")
};
println!("{}",t.print_summry());
}
extra.rs
mod main {
include!("main.rs");
}
pub struct Feed {
pub name: String,
pub message: String
}
impl Summary for Feed {
fn print_summry(&self) -> String {
format!("{}: {}",self.name,self.message)
}
}
Elements of parent module can be accessed with help of super. Therefore adding use super::*; on top or using super::Summary both works. But it is better to use super::Summary as it don't include everything of main.rs.

In memory database design

I am trying to create an in-memory database using HashMap. I have a struct Person:
struct Person {
id: i64,
name: String,
}
impl Person {
pub fn new(id: i64, name: &str) -> Person {
Person {
id: id,
name: name.to_string(),
}
}
pub fn set_name(&mut self, name: &str) {
self.name = name.to_string();
}
}
And I have struct Database:
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::Mutex;
struct Database {
db: Arc<Mutex<HashMap<i64, Person>>>,
}
impl Database {
pub fn new() -> Database {
Database {
db: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn add_person(&mut self, id: i64, person: Person) {
self.db.lock().unwrap().insert(id, person);
}
pub fn get_person(&self, id: i64) -> Option<&mut Person> {
self.db.lock().unwrap().get_mut(&id)
}
}
And code to use this database:
let mut db = Database::new();
db.add_person(1, Person::new(1, "Bob"));
I want to change person's name:
let mut person = db.get_person(1).unwrap();
person.set_name("Bill");
The complete code in the Rust playground.
When compiling, I get a problem with Rust lifetimes:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:39:9
|
39 | self.db.lock().unwrap().get_mut(&id)
| ^^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough
40 | }
| - temporary value only lives until here
|
note: borrowed value must be valid for the anonymous lifetime #1 defined on the method body at 38:5...
--> src/main.rs:38:5
|
38 | / pub fn get_person(&self, id: i64) -> Option<&mut Person> {
39 | | self.db.lock().unwrap().get_mut(&id)
40 | | }
| |_____^
How to implement this approach?
The compiler rejects your code because it violates the correctness model enforced by Rust and could cause crashes. For one, if get_person() were allowed to compile, one might call it from two threads and modify the underlying object without the protection of the mutex, causing data races on the String object inside. Worse, one could wreak havoc even in a single-threaded scenario by doing something like:
let mut ref1 = db.get_person(1).unwrap();
let mut ref2 = db.get_person(1).unwrap();
// ERROR - two mutable references to the same object!
let vec: Vec<Person> = vec![];
vec.push(*ref1); // move referenced object to the vector
println!(*ref2); // CRASH - object already moved
To correct the code, you need to adjust your design to satisfy the following constraints:
No reference can be allowed to outlive the referred-to object;
During the lifetime of a mutable reference, no other reference (mutable or immutable) to the object may exist..
The add_person method already complies with both rules because it eats the object you pass it, moving it to the database.
What if we modified get_person() to return an immutable reference?
pub fn get_person(&self, id: i64) -> Option<&Person> {
self.db.lock().unwrap().get(&id)
}
Even this seemingly innocent version still doesn't compile! That is because it violates the first rule. Rust cannot statically prove that the reference will not outlive the database itself, since the database is allocated on the heap and reference-counted, so it can be dropped at any time. But even if it were possible to somehow explicitly declare the lifetime of the reference to one that provably couldn't outlive the database, retaining the reference after unlocking the mutex would allow data races. There is simply no way to implement get_person() and still retain thread safety.
A thread-safe implementation of a read can opt to return a copy of the data. Person can implement the clone() method and get_person() can invoke it like this:
#[derive(Clone)]
struct Person {
id: i64,
name: String
}
// ...
pub fn get_person(&self, id: i64) -> Option<Person> {
self.db.lock().unwrap().get(&id).cloned()
}
This kind of change won't work for the other use case of get_person(), where the method is used for the express purpose of obtaining a mutable reference to change the person in the database. Obtaining a mutable reference to a shared resource violates the second rule and could lead to crashes as shown above. There are several ways to make it safe. One is by providing a proxy in the database for setting each Person field:
pub fn set_person_name(&self, id: i64, new_name: String) -> bool {
match self.db.lock().unwrap().get_mut(&id) {
Some(mut person) => {
person.name = new_name;
true
}
None => false
}
}
As the number of fields on Person grows, this would quickly get tedious. It could also get slow, as a separate mutex lock would have to be acquired for each access.
There is fortunately a better way to implement modification of the entry. Remember that using a mutable reference violates the rules unless Rust can prove that the reference won't "escape" the block where it is being used. This can be ensured by inverting the control - instead of a get_person() that returns the mutable reference, we can introduce a modify_person() that passes the mutable reference to a callable, which can do whatever it likes with it. For example:
pub fn modify_person<F>(&self, id: i64, f: F) where F: FnOnce(Option<&mut Person>) {
f(self.db.lock().unwrap().get_mut(&id))
}
The usage would look like this:
fn main() {
let mut db = Database::new();
db.add_person(1, Person::new(1, "Bob"));
assert!(db.get_person(1).unwrap().name == "Bob");
db.modify_person(1, |person| {
person.unwrap().set_name("Bill");
});
}
Finally, if you're worried about the performance of get_person() cloning Person for the sole reason of inspecting it, it is trivial to create an immutable version of modify_person that serves as a non-copying alternative to get_person():
pub fn read_person<F, R>(&self, id: i64, f: F) -> R
where F: FnOnce(Option<&Person>) -> R {
f(self.db.lock().unwrap().get(&id))
}
Besides taking a shared reference to Person, read_person is also allowing the closure to return a value if it chooses, typically something it picks up from the object it receives. Its usage would be similar to the usage of modify_person, with the added possibility of returning a value:
// if Person had an "age" field, we could obtain it like this:
let person_age = db.read_person(1, |person| person.unwrap().age);
// equivalent to the copying definition of db.get_person():
let person_copy = db.read_person(1, |person| person.cloned());
This post use the pattern cited as "inversion of control" in the well explained answer and just add only sugar for demonstrating another api for an in-memory db.
With a macro rule it is possible to expose a db client api like that:
fn main() {
let db = Database::new();
let person_id = 1234;
// probably not the best design choice to duplicate the person_id,
// for the purpose here is not important
db.add_person(person_id, Person::new(person_id, "Bob"));
db_update!(db #person_id => set_name("Gambadilegno"));
println!("your new name is {}", db.get_person(person_id).unwrap().name);
}
My opinionated macro has the format:
<database_instance> #<object_key> => <method_name>(<args>)
Below the macro implementation and the full demo code:
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::Mutex;
macro_rules! db_update {
($db:ident # $id:expr => $meth:tt($($args:tt)*)) => {
$db.modify_person($id, |person| {
person.unwrap().$meth($($args)*);
});
};
}
#[derive(Clone)]
struct Person {
id: u64,
name: String,
}
impl Person {
pub fn new(id: u64, name: &str) -> Person {
Person {
id: id,
name: name.to_string(),
}
}
fn set_name(&mut self, value: &str) {
self.name = value.to_string();
}
}
struct Database {
db: Arc<Mutex<HashMap<u64, Person>>>, // access from different threads
}
impl Database {
pub fn new() -> Database {
Database {
db: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn add_person(&self, id: u64, person: Person) {
self.db.lock().unwrap().insert(id, person);
}
pub fn modify_person<F>(&self, id: u64, f: F)
where
F: FnOnce(Option<&mut Person>),
{
f(self.db.lock().unwrap().get_mut(&id));
}
pub fn get_person(&self, id: u64) -> Option<Person> {
self.db.lock().unwrap().get(&id).cloned()
}
}

Lifetime Issue with Associated Types

I've been pulling my hair out over the last week due to this incredibly annoying issue with lifetimes.
The problem occurs when I try to put a reference to a Buffer inside a DataSource, which is then referenced to a DrawCommand. I'm getting the error: vertex_data_source does not live long enough.
src/main.rs:65:23: 65:41 error:
src/main.rs:65 data_source: &vertex_data_source
^~~~~~~~~~~~~~~~~~
src/main.rs:60:51: 67:2 note: reference must be valid for the block suffix following statement 3 at 60:50...
src/main.rs:60 let vertices = VertexAttributes::new(&buffer);
src/main.rs:61
src/main.rs:62 let vertex_data_source = factory.create_data_source(vertices);
src/main.rs:63
src/main.rs:64 let command: DrawCommand<ResourcesImpl> = DrawCommand {
src/main.rs:65 data_source: &vertex_data_source
...
src/main.rs:62:67: 67:2 note: ...but borrowed value is only valid for the block suffix following statement 4 at 62:66
src/main.rs:62 let vertex_data_source = factory.create_data_source(vertices);
src/main.rs:63
src/main.rs:64 let command: DrawCommand<ResourcesImpl> = DrawCommand {
src/main.rs:65 data_source: &vertex_data_source
src/main.rs:66 };
src/main.rs:67 }
It says vertex_data_source has to be valid for the block suffix following statement 3 at line 60. My interpretation of that error is that vertex_data_source should be defined before line 60. But to create the vertex_data_source in the first place I need access to those VertexAttributes on line 60, so I can't just swap the order around.
I feel like all the 'a lifetimes sprinkled over my code need to be split into 2 or maybe just removed, however I've tried every combination that seemed sensible and I'm not out of ideas.
Below is a greatly simplified example of my code that demonstrates the problem. I would really appreciate a sanity check and hopefully a fresh mind might be able to spot the issue. (every time before a few days of fiddling has produced a fix but this time I'm stumped).
use std::cell::RefCell;
use std::marker::PhantomData;
pub struct DrawCommand<'a, R: Resources<'a>> {
pub data_source: &'a R::DataSource
}
pub trait Resources<'a> {
type DataSource: 'a;
type Buffer: 'a;
}
pub struct DataSource<'a> {
id: u32,
attributes: Vec<VertexAttributes<'a, ResourcesImpl<'a>>>,
current_element_array_buffer_binding: RefCell<Option<Buffer<'a>>>
}
pub struct Buffer<'a> {
context: &'a GraphicsContextImpl
}
pub struct GraphicsContextImpl;
pub struct ResourcesImpl<'a> {
phantom: PhantomData<&'a u32> // 'a is the lifetime of the context reference
}
impl<'a> Resources<'a> for ResourcesImpl<'a> {
type Buffer = Buffer<'a>;
type DataSource = DataSource<'a>;
}
struct Factory<'a> {
context: &'a GraphicsContextImpl
}
impl<'a> Factory<'a> {
/// Creates a buffer
fn create_buffer<T>(&self) -> Buffer<'a> {
Buffer {
context: self.context
}
}
fn create_data_source(&self, attributes: Vec<VertexAttributes<'a, ResourcesImpl<'a>>>) -> DataSource<'a> {
DataSource {
id: 0,
attributes: attributes,
current_element_array_buffer_binding: RefCell::new(None)
}
}
}
fn main() {
let context = GraphicsContextImpl;
let factory = Factory {
context: &context
};
let buffer = factory.create_buffer::<u32>();
let vertices = VertexAttributes::new(&buffer);
let vertex_data_source = factory.create_data_source(vec!(vertices));
let command: DrawCommand<ResourcesImpl> = DrawCommand {
data_source: &vertex_data_source
};
}
pub struct VertexAttributes<'a, R: Resources<'a>> {
pub buffer: &'a R::Buffer,
}
impl<'a, R: Resources<'a>> VertexAttributes<'a, R> {
pub fn new(buffer: &'a R::Buffer) -> VertexAttributes<'a, R> {
VertexAttributes {
buffer: buffer
}
}
}
Thanks very much in advance.
EDIT:
I've updated the code to better reflect my actual implementation.
By the way - replacing this:
let vertex_data_source = factory.create_data_source(vec!(vertices));
With this:
let vertex_data_source = DataSource {
id: 0,
attributes: vec!(vertices),
current_element_array_buffer_binding: RefCell::new(None)
};
Doesn't solve the issue.
This allows your example to compile:
pub struct DrawCommand<'a : 'b, 'b, R: Resources<'a>> {
pub data_source: &'b R::DataSource
}
However, I'm finding it extremely difficult to create a more minimal example. As best I can determine, you have an issue because you are declaring that you will hold a reference to an item that itself has a reference, and that those two references need to have a common lifetime ('a). Through some combination of the other lifetimes, this is actually impossible.
Adding a second lifetime allows the reference to the DataSource to differ from the reference of the DataSource itself.
I'm still going to try to create a more minified example.

Resources