Skip to content

Commit d8d1f9b

Browse files
Merge pull request #1 from Eugene-Usachev/main
Init
2 parents 2aff759 + b49b3aa commit d8d1f9b

12 files changed

Lines changed: 708 additions & 3 deletions

File tree

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
name: Bug report
3+
about: Create a report to help us improve
4+
title: ''
5+
labels: bug
6+
assignees: ''
7+
8+
---
9+
10+
### **Describe the Bug**
11+
<!-- A clear and concise description of what the bug is. Include any relevant details. -->
12+
13+
### **Steps to Reproduce**
14+
<!-- Provide a clear, step-by-step process to reproduce the issue, including code snippets or commands if applicable. -->
15+
16+
### **Expected Behavior**
17+
<!-- A clear description of what you expected to happen. -->
18+
19+
### **Actual Behavior**
20+
<!-- A clear description of what actually happened. Include any error messages or outputs. -->
21+
22+
### **Environment**
23+
<!-- Provide the environment details where the issue occurred. For example: -->
24+
- **Rust Version**:
25+
- **Orengine crate version**:
26+
- **Operating System**:
27+
- **Other Dependencies**:
28+
29+
### **Relevant Logs/Output**
30+
<!-- Paste any relevant logs, error messages, or outputs here. -->
31+
32+
<details>
33+
<summary>Click to expand</summary>
34+
35+
```rust
36+
// Add your logs or output here.
37+
```
38+
</details>
39+
40+
### **Code Sample**
41+
<!-- Provide a minimal, complete, and verifiable code sample that reproduces the issue. -->
42+
43+
```rust
44+
// Minimal code example that causes the bug.
45+
```
46+
47+
### **Possible Solution**
48+
<!-- If you have an idea of how to fix the issue, describe it here. Otherwise, you can leave this section empty. -->
49+
50+
### **Additional Context**
51+
<!-- Add any other context or screenshots about the problem here. --> <!-- If applicable, link to related issues or pull requests: - #123 - #456 -->
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
name: Feature request
3+
about: Suggest an idea for this project
4+
title: ''
5+
labels: enhancement
6+
assignees: ''
7+
8+
---
9+
10+
### **Description**
11+
<!-- A clear and concise description of the feature or enhancement you’re requesting. Explain why it would be useful. -->
12+
13+
### **Use Case**
14+
<!-- Explain how this feature will be used and why it is important. Provide examples or scenarios if applicable. -->
15+
16+
### **Proposed Solution**
17+
<!-- If you have an idea of how the feature can be implemented, describe it here. You can include code snippets, APIs, or design suggestions. -->
18+
19+
### **Alternatives Considered**
20+
<!-- List any alternative approaches or workarounds you’ve tried or thought of. -->
21+
22+
### **Additional Context**
23+
<!-- Add any other context, screenshots, or references to related issues or PRs here. -->

.github/workflows/rust.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Rust
2+
3+
on:
4+
push:
5+
branches: [ "main" ]
6+
pull_request:
7+
branches: [ "main" ]
8+
9+
env:
10+
CARGO_TERM_COLOR: always
11+
12+
jobs:
13+
test-linux:
14+
runs-on: ubuntu-latest
15+
timeout-minutes: 10
16+
steps:
17+
- name: Checkout code
18+
uses: actions/checkout@v4
19+
20+
- name: Set up Rust
21+
uses: actions-rs/toolchain@v1
22+
with:
23+
profile: minimal
24+
toolchain: stable
25+
override: true
26+
27+
- name: Run tests
28+
run: |
29+
cargo test
30+
cargo test --release
31+
32+
code-style-check:
33+
runs-on: ubuntu-latest
34+
timeout-minutes: 10
35+
steps:
36+
- name: Checkout code
37+
uses: actions/checkout@v4
38+
39+
- name: Set up Rust
40+
uses: actions-rs/toolchain@v1
41+
with:
42+
profile: minimal
43+
toolchain: stable
44+
override: true
45+
46+
- name: Run tests
47+
run: |
48+
cargo fmt --all --check
49+
cargo clippy
50+
cargo doc

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ target/
1818
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
1919
# and can be added to the global gitignore or merged into this file. For a more nuclear
2020
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
21-
#.idea/
21+
.idea/

CODE_OF_CONDUCT.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Code of Conduct
2+
3+
This project adheres to the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct).
4+
This describes the minimum behavior expected from all contributors.

CONTRIBUTING.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
__Thank you for helping to make Orengine better!__
2+
3+
__Before the pull request, do the following:__
4+
5+
- Check a code quality via `cargo clippy`, `cargo doc` and `cargo fmt --all --check`;
6+
7+
- Run `cargo test` and `cargo test --release`;

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "manually-static"
3+
version = "1.1.0"
4+
edition = "2021"
5+
license = "MIT"
6+
description = """
7+
Provides `ManualStatic` that is a wrapper which allows to manually manage `'static` lifetimes.
8+
It is unsafe but with `debug_assertions` it panics when the usage is wrong.
9+
"""
10+
rust-version = "1.70.0"
11+
authors = ["Eugene Usachev <https://github.com/Eugene-Usachev> and orengine contributors <team@orengine>"]
12+
readme = "README.md"
13+
repository = "https://github.com/Eugene-Usachev/manually-static"
14+
categories = ["memory-management"]
15+
keywords = ["memory-management", "static", "lifetime", "unsafe"]
16+
17+
[dependencies]

README.md

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,120 @@
1-
# manual-static
2-
Provides `ManualStatic` that is a wrapper which allows to manually manage `'static` lifetimes. It is unsafe but with `debug_assertions` it panics when the usage is wrong.
1+
# Manually-static: Bridging the `'static` Gap with Debug-Time Safety
2+
3+
This crate provides `ManuallyStatic<T>`,
4+
a powerful wrapper that allows you to manually manage `'static` lifetimes for your data.
5+
While it uses `unsafe` under the hood, it also uses robust debug-time checks that panic on incorrect usage.
6+
This means you can confidently assert `'static` guarantees in your code,
7+
knowing that misuse will be caught during development and testing.
8+
9+
## Why `manually-static`?
10+
In concurrent programming with threads or asynchronous operations, data often needs to be `'static` to
11+
be shared or moved across task boundaries.
12+
However, sometimes you have a logical
13+
guarantee that a reference will live for the entire program's duration,
14+
even if you can't easily prove it to the compiler through standard means.
15+
16+
`manually-static` empowers you to:
17+
18+
- Opt-in to manual `'static` management: Take control when the compiler's strictness becomes a hurdle.
19+
20+
- Catch errors early: Leverage `debug_assertions` to detect use-after-free scenarios or other incorrect
21+
dereferencing before they become hard-to-debug runtime crashes in production.
22+
23+
- Simplify complex lifetime annotations: Reduce boilerplate and make your code more readable in scenarios
24+
where `'static` is implicitly guaranteed.
25+
26+
## Usage
27+
28+
First, add `manually-static` to your `Cargo.toml`:
29+
30+
```toml
31+
[dependencies]
32+
manually-static = "1.0.1" # Or the latest version
33+
```
34+
35+
## Threading Example (Illustrating `'static` need)
36+
37+
```rust
38+
use manually_static::ManuallyStatic;
39+
use std::thread;
40+
use std::time::Duration;
41+
42+
struct AppConfig {
43+
version: String,
44+
}
45+
46+
fn main() {
47+
let config = ManuallyStatic::new(AppConfig {
48+
version: String::from("1.0.0"),
49+
});
50+
51+
// Get a 'static reference to the config.
52+
// This is where ManuallyStatic shines, allowing us to pass
53+
// a reference that the compiler would normally complain about
54+
// without complex ownership transfers or Arc for simple reads.
55+
let config_ref = config.get_ref();
56+
57+
let handle = thread::spawn(move || {
58+
// In this thread, we can safely access the config via the 'static reference.
59+
// In debug builds, if `config` (the original ManuallyStatic) was dropped
60+
// before this thread accessed it, it would panic.
61+
62+
thread::sleep(Duration::from_millis(100)); // Simulate some work
63+
64+
println!("Thread: App Version: {}", config_ref.version);
65+
});
66+
67+
handle.join().unwrap();
68+
69+
// config is dropped here after the thread has finished
70+
}
71+
```
72+
73+
## Example with allocating the data on the heap
74+
75+
```rust
76+
use manually_static::ManuallyStaticPtr;
77+
use std::sync::Mutex;
78+
use std::array;
79+
80+
const N: usize = 10280;
81+
const PAR: usize = 16;
82+
83+
#[allow(dead_code, reason = "It is an example.")]
84+
struct Pool(Mutex<([Vec<u8>; N], usize)>);
85+
86+
fn main() {
87+
let pool = ManuallyStaticPtr::new(Pool(Mutex::new((array::from_fn(|_| Vec::new()), 0))));
88+
let mut joins = Vec::with_capacity(PAR);
89+
90+
for _ in 0..PAR {
91+
#[allow(unused_variables, reason = "It is an example.")]
92+
let pool = pool.clone();
93+
94+
joins.push(std::thread::spawn(move || {
95+
/* ... do some work ... */
96+
}));
97+
}
98+
99+
for join in joins {
100+
join.join().unwrap();
101+
}
102+
103+
unsafe { pool.free(); }
104+
}
105+
```
106+
107+
## ⚠️ Important Considerations
108+
109+
- `unsafe` under the hood: While `manually-static` provides debug-time checks,
110+
the underlying mechanism involves raw pointers.
111+
In release builds, these checks are absent,
112+
and misusing `ManuallyStaticRef` after the original `ManuallyStatic` has been dropped
113+
will lead to undefined behavior (UB).
114+
- Use responsibly: This crate is intended for specific scenarios where you have a strong,
115+
provable-by-logic guarantee about the lifetime of your data,
116+
but the compiler's static analysis cannot infer it.
117+
Avoid using it as a general workaround for lifetime errors without fully understanding the implications.
118+
119+
`manually-static` is your trusty companion for those tricky `'static` lifetime puzzles,
120+
offering a powerful blend of flexibility and debug-time safety!

src/lib.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//! # `ManuallyStatic`
2+
//!
3+
//! `ManuallyStatic` is a crate that provides a way to simulate a 'static lifetime
4+
//! where you want runtime checks for use-after-free in debug environments.
5+
//!
6+
//! Read [`ManuallyStatic`'s documentation](ManuallyStatic)
7+
//! and [`ManuallyStaticPtr`'s documentation](ManuallyStaticPtr) for more information.
8+
#![deny(clippy::all)]
9+
#![deny(clippy::assertions_on_result_states)]
10+
#![deny(clippy::match_wild_err_arm)]
11+
#![deny(clippy::allow_attributes_without_reason)]
12+
#![warn(clippy::pedantic)]
13+
#![warn(clippy::nursery)]
14+
#![warn(clippy::cargo)]
15+
#![allow(
16+
clippy::missing_const_for_fn,
17+
reason = "Since we cannot make a constant function non-constant after its release,
18+
we need to look for a reason to make it constant, and not vice versa."
19+
)]
20+
#![allow(
21+
clippy::must_use_candidate,
22+
reason = "It is better to developer think about it."
23+
)]
24+
#![allow(
25+
clippy::missing_errors_doc,
26+
reason = "Unless the error is something special,
27+
the developer should document it."
28+
)]
29+
30+
mod ptr;
31+
mod stack_and_ref;
32+
33+
pub use ptr::ManuallyStaticPtr;
34+
pub use stack_and_ref::{ManuallyStatic, ManuallyStaticRef};

0 commit comments

Comments
 (0)