Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/jkelleyrtp/rust-specialization
Clever ways you can specialize in Rust
https://github.com/jkelleyrtp/rust-specialization
Last synced: about 1 month ago
JSON representation
Clever ways you can specialize in Rust
- Host: GitHub
- URL: https://github.com/jkelleyrtp/rust-specialization
- Owner: jkelleyrtp
- Created: 2023-12-06T09:52:29.000Z (12 months ago)
- Default Branch: master
- Last Pushed: 2023-12-06T10:29:17.000Z (12 months ago)
- Last Synced: 2023-12-06T11:25:45.243Z (12 months ago)
- Language: Rust
- Size: 8.79 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# abusing specialization to the max
this crate showcases some interesting peculiarities of how Rust's specialization lets you do some trickery.
## Showcased here:
- Marker: specialize against a concrete type (like a Signal) to have different behavior from the general more blanket impl
- BlanketBlanket: have two blanket impls but specialize on the one where the implemented type implements a trait of interest. In this case, any type that's Copy
- MaybePartialEq: A form of BlanketBlanket where we can compare non-comparable types
- MaybeStatic: doesn't work (yet) but the goal here is to specialize on a type if it has an internal lifetime.## What motiviated this?
In Dioxus, we have this `inline_props` macro that lets you declare a struct using arguments of a function for short-and-sweet components.
```rust
#[component]
fn Example(age: i32) -> Element {
//...
}
```This has some weird side-effects though - props in Dioxus are borrowed between renders. So everywhere `name` and `age` are used, they get borrowed. This means when dealing with async, you have to clone them before using them, since Futures in Dioxus must be `'static`
```rust
#[component]
fn Example(age: i32) -> Element {
// age is actually &i32 due to how structs are unpacked
// we need to copy it
let age = *age;
cx.spawn(async move {
print!("{age}");
});
// ..
}
```However, we can't just deref everything since some things can't be copied. This is particularly bad for heavy things where an implicit clone would cost a lot.
```rust
#[component]
fn Example(name: String) -> Element {
// name is actually &String due to how structs are unpacked
let name: String = name.clone();cx.spawn(async move {
print!("{name}");
});
// ..
}
```So, we need something akin to `maybe_copy`. There is not a trait for this. The code in this repository showcases a new dual-trait combo that allows you to call `rebind` on any T, and return Copied T when T is Copy, and &T when T is not Copy.
```rust
#[component]
fn Example(age: i32, name: String) -> Element {
// No lifetime attached!
let age: i32 = age.rebind();// This type can't be copied so it keeps its lifetime
let name: &String = name.rebind();
}
```This is *particularly* important when dealing with signals. Signals are copy types but would normally have their lifetimes stuck to them, making code like this impossible:
```rust
#[component]
fn Example(name: Signal) -> Element {
let uppercase = memo(move || name.to_ascii_uppercase());
}
```With the rebind traits, we can fix this for all arguments while preserving the existing behavior for Clone types.
```rust
struct Props {
name: Signal
}fn Example(cx: Scope) -> Element {
// generated by the component macro
let name = (&cx.props.name).rebind();// now we can freely use name everywhere since its Copy
let uppercase = memo(move || name.to_ascii_uppercase());
}
```Cool, right?