Python Package Structure
In Python, you organize code using files and directories. Each directory with an__init__.py
file becomes a package:
my_project/ ├── __init__.py ├── main.py ├── utils/ │ ├── __init__.py │ ├── helpers.py │ └── math_ops.py └── models/ ├── __init__.py ├── user.py └── product.py
Basic Module Structure
# utils/math_ops.py
def add(a, b):
return a + b
def multiply(a, b):
return a * b
# utils/__init__.py
from .math_ops import add, multiply
# main.py
from utils import add, multiply
# or
from utils.math_ops import add
result = add(5, 3)
// src/utils/math_ops.rs
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
pub fn multiply(a: i32, b: i32) -> i32 {
a * b
}
// src/utils/mod.rs
pub mod math_ops;
pub use math_ops::{add, multiply};
// src/main.rs
mod utils;
use utils::{add, multiply};
fn main() {
let result = add(5, 3);
}
Rust Module System
Rust uses a different approach. Instead of relying on the file system alone, Rust has explicit module declarations:
my_project/ ├── Cargo.toml └── src/ ├── main.rs ├── utils/ │ ├── mod.rs │ └── math_ops.rs └── models/ ├── mod.rs ├── user.rs └── product.rs
Module Declaration and Visibility
One key difference is that Rust requires explicit module declarations and visibility controls:
Visibility Control
# Python: Everything is public by convention
# utils/helpers.py
def public_function():
return "I'm public"
def _private_function(): # Convention: starts with _
return "I'm private by convention"
# main.py
from utils.helpers import public_function, _private_function
# Both work, privacy is just convention
// Rust: Explicit visibility control
// src/utils/helpers.rs
pub fn public_function() -> &'static str {
"I'm public"
}
fn private_function() -> &'static str { // No 'pub' = private
"I'm private"
}
// src/main.rs
mod utils;
use utils::helpers::public_function;
// use utils::helpers::private_function; // ❌ Error! Not public
Importing and Using Code
Both languages have ways to import and use code from other modules, but the syntax and behavior differ:
Import Syntax Comparison
# Python imports
import json
import os.path
from collections import defaultdict
from typing import List, Dict
# Relative imports
from .utils import helper_function
from ..models import User
# Aliasing
import numpy as np
from datetime import datetime as dt
// Rust use statements
use std::collections::HashMap;
use std::fs;
use std::path::Path;
// Multiple imports from same module
use std::io::{self, Read, Write};
// Relative imports (within your crate)
use crate::utils::helper_function;
use super::models::User;
// Aliasing
use std::collections::HashMap as Map;
Re-exports and Module Organization
Both languages support re-exporting symbols to create cleaner APIs:
Re-exports
# Python re-exports
# mylib/__init__.py
from .core import CoreClass
from .utils import helper_function
from .advanced import AdvancedFeature
# Users can import directly from mylib
# from mylib import CoreClass, helper_function
# Or create aliases
from .core import CoreClass as Core
// Rust re-exports
// src/lib.rs
mod core;
mod utils;
mod advanced;
// Re-export for easier access
pub use core::CoreStruct;
pub use utils::helper_function;
pub use advanced::AdvancedFeature;
// Users can import directly
// use mylib::{CoreStruct, helper_function};
// Or create aliases
pub use core::CoreStruct as Core;
Inline Modules vs Separate Files
Rust allows you to define modules inline or in separate files, giving you more flexibility than Python:
Inline vs File Modules
# Python: Always separate files for modules
# You can't define a module inline in Python
# Each .py file is a module
# utils.py
def helper():
pass
# main.py
import utils
utils.helper()
// Rust: Inline modules
mod utils {
pub fn helper() {
println!("Helper function");
}
mod private_submodule {
pub fn internal_function() {
println!("Internal");
}
}
}
fn main() {
utils::helper();
}
// Or separate files (like Python)
// mod utils; // Looks for utils.rs or utils/mod.rs
Best Practices Comparison
Python Best Practices
- • Use
__init__.py
to control package API - • Follow PEP 8 naming conventions
- • Use relative imports within packages
- • Keep modules focused and cohesive
- • Use
__all__
to controlfrom module import *
Rust Best Practices
- • Use
mod.rs
orlib.rs
to control module API - • Follow Rust naming conventions (snake_case)
- • Use
pub use
for re-exports - • Keep modules focused and cohesive
- • Use
crate::
for absolute paths within your crate
Key Takeaways
- Explicit vs Implicit: Rust requires explicit module declarations, Python uses file system
- Visibility: Rust has compile-time privacy, Python uses conventions
- Re-exports: Both support clean APIs through re-exports
- Flexibility: Rust allows inline modules, Python requires separate files
- Organization: Both support hierarchical code organization
Pro Tip: Start with Rust's module system by thinking about your Python package structure, then add explicit mod
declarations and pub
keywords where needed. The compiler will guide you through any issues!