Skip to main content

Curve

Enum Curve 

Source
#[non_exhaustive]
pub enum Curve<'a> { Linear { step: Option<&'a str>, }, Logarithmic { step: Option<&'a str>, }, }
Expand description

One field of a form.

Borrowed rather than owned: a description is built, read once by a renderer, and dropped. Nothing here outlives the screen it describes.

§What it carries, and what it does not

Stated here so the next renderer does not re-ask, which is what the first two both did. It carries everything a renderer needs to draw the field: its kind, what it is called, what it is asked for, its standing help, what is wrong with it now, whether it is compulsory, whether it hides behind a disclosure, its ghost text, and the options it offers.

It does not carry the current value, and it is not going to. That is the one thing here that is genuinely renderer state: a webview reads it back out of the DOM, an immediate-mode renderer holds a &mut to the app’s own field and writes through it, and a terminal keeps an edit buffer. A description that carried the value would have to carry a way to write it back, at which point it is a form model and no longer a description.

Constraints are here and enforcement is not, which is one line rather than two. required, max_length, min and max are facts about the question, so a renderer can emit its host’s idiom for each — an HTML attribute, a marked label, a clamped spinner — and the platform helps the user before anything is submitted. Deciding that a value is wrong stays with whoever validated, and error is that decision arriving back.

The set stops before pattern, and stops there on both tests at once. A regex has an honest answer in a webview and none anywhere else: egui would have to run it per keystroke and decide what a half-typed value means, which is enforcement wearing description’s clothes. And it is one site in goingson and none in Balanced Breakfast, against 8 and 1 for maxlength. Measured 2026-08-09, 2cbad3e2.

How a slider’s position becomes its value, and how finely it moves.

The data of a slider is a fraction and a function taking numbers to numbers. Stated by Max 2026-08-21, and it corrects a reading this crate had carried since FieldKind::Range arrived at 0.28.0: min and max were never the control’s extent. A slider’s extent is always 0 to 1 — a thumb at 40% of a track — and the bounds are f(0) and f(1). Linear is the constant-slope case, which is exactly why nobody noticed the function was there: when f is min + t * (max - min) the extent and the bounds coincide numerically and the mapping is invisible.

So this is not a scale flag bolted onto a range. Every range described before it had a mapping, and four renderers each hard-coded the same one.

§Why a closed family and not a function

fn(f64) -> f64 is the literal reading and it does not survive the description boundary. A fn pointer cannot be emitted into a browser, and it cannot be compared or hashed in a way that means anything, which this struct needs. A named family is the same semantics with arbitrary closures given up, and nothing measured wants one: the tree has a single non-linear shape across five controls and no second shape at all.

§Why the step is here

Max, in the same breath: if the family is prescriptive anyway, the step spacing belongs in it. On a slider the granularity and the mapping are one decision — a curve chosen without saying how finely it moves is half an answer — and holding them apart is what let a 0-to-1 threshold ship as a two-position control, since the host default of 1 was applied to a mapping nobody had named. It also un-overloads Field::step, which stays as it was for a typed value, where there is no mapping and the granularity is a plain fact about the number.

A future curve carrying a fact of its own — an exponent, an inflection — puts it in its own variant rather than on the struct, which is the second reason this shape is right.

The step is in the value’s own units under every curve. What a curve changes is the mapping, not the units the granularity is measured in: a step of 0.001 on an envelope time is three decimals whether the track is logarithmic or not, and a renderer that reads the step for display precision keeps reading it the same way.

Added 0.32.0, from audiofiles’ ADSR envelope and its storage cap picker.

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

Linear

Constant slope: f(t) = min + t * (max - min).

What every described range meant before this enum existed, and the default, so a site that says nothing is correct unchanged.

Fields

§step: Option<&'a str>

The granularity, in the value’s own units. None is the host’s own.

§

Logarithmic

Constant ratio: f(t) = min * (max / min).powf(t).

The mapping for a question whose extent spans orders of magnitude and whose interesting half is the small end. audiofiles’ envelope times run 0.001 to 5 seconds, where a 5 ms attack and a 50 ms attack are audibly different instruments and a linear track puts both inside its first one percent.

§It needs positive bounds

A constant ratio is undefined across zero, so this asks for min > 0. A range that does not have that is mapped Linearly instead — see value_at. Stated rather than enforced, the way every other constraint in this crate is, and it is not a hypothetical: an envelope’s sustain is a 0-to-1 level and is linear for this reason rather than by oversight.

Fields

§step: Option<&'a str>

The granularity, in the value’s own units. None is the host’s own.

Implementations§

Source§

impl<'a> Curve<'a>

Source

pub const fn step(self) -> Option<&'a str>

The granularity this curve moves in, whichever curve it is.

Every variant carries one, so reading it does not need a match at each of the four renderers.

Source

pub fn is_ratio(self, min: f64, max: f64) -> bool

Whether this curve maps as a constant ratio given these bounds.

The bounds are the argument because Logarithmic is a request rather than a guarantee: it needs 0 < min < max, and a range that does not have that is drawn linearly. A renderer asks this instead of matching on the variant, so the fallback is decided in one place rather than four.

Source

pub fn value_at(self, position: f64, min: f64, max: f64) -> f64

The value at a position along the track, where position is 0 to 1.

f. The whole point of the type, and it lives here rather than in each renderer so that a terminal’s bar, an egui slider and a browser’s input cannot disagree about where a value sits.

A position outside 0 to 1 is clamped, and bounds that are equal or inverted give min back: a track with no extent has one value on it.

Source

pub fn position_of(self, value: f64, min: f64, max: f64) -> f64

The position a value sits at, where the answer is 0 to 1.

f inverted, which is what a renderer needs to draw a value it was handed. Same clamping and the same degenerate answer as value_at.

Trait Implementations§

Source§

impl<'a> Clone for Curve<'a>

Source§

fn clone(&self) -> Curve<'a>

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<'a> Copy for Curve<'a>

Source§

impl<'a> Debug for Curve<'a>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Curve<'_>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<'a> Eq for Curve<'a>

Source§

impl<'a> Hash for Curve<'a>

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl<'a> PartialEq for Curve<'a>

Source§

fn eq(&self, other: &Curve<'a>) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl<'a> StructuralPartialEq for Curve<'a>

Auto Trait Implementations§

§

impl<'a> Freeze for Curve<'a>

§

impl<'a> RefUnwindSafe for Curve<'a>

§

impl<'a> Send for Curve<'a>

§

impl<'a> Sync for Curve<'a>

§

impl<'a> Unpin for Curve<'a>

§

impl<'a> UnsafeUnpin for Curve<'a>

§

impl<'a> UnwindSafe for Curve<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.