pub struct Field<'a> {Show 13 fields
pub kind: FieldKind,
pub name: &'a str,
pub label: &'a str,
pub hint: Option<&'a str>,
pub error: Option<&'a str>,
pub placeholder: Option<&'a str>,
pub options: &'a [Choice<'a>],
pub required: bool,
pub max_length: Option<u32>,
pub min: Option<&'a str>,
pub max: Option<&'a str>,
pub step: Option<&'a str>,
pub extended: bool,
}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.
Fields§
§kind: FieldKindWhat kind of value it takes.
name: &'a strThe name the value is submitted under.
label: &'a strWhat the user is asked for.
hint: Option<&'a str>Standing help, shown whether or not anything is wrong.
error: Option<&'a str>What is currently wrong with the value.
placeholder: Option<&'a str>Ghost text shown while the field is empty.
User-facing text, and it sits with label and hint rather than with
the value because it is a property of the question and not of the
answer. It lived renderer-side in makeover-webview until 0.8.0 for one
reason and it was not a reading on where it belonged: adding a field to
a published struct is a breaking change.
Not a substitute for a label. A field labelled only by its placeholder loses its label the moment anything is typed, and no renderer here can make that not happen, so the description keeps both.
options: &'a [Choice<'a>]The options offered, in the order they are offered.
Empty for every kind FieldKind::offers_options rejects. A field
described with no options is sayable on purpose: it is what an app with
an unfinished-loading option list actually has, and a renderer showing
an empty control says so on screen rather than in a log.
Which option is current is not here. That is the value, and the value is renderer state.
required: boolWhether the form refuses to submit without it.
max_length: Option<u32>The longest the value may be, in characters.
Added 0.11.0 with min and max, joining
required, which had been the only constraint here
since before the crate wrote down that it carried none.
min: Option<&'a str>The lowest value accepted, as the host would write it.
Text rather than a number, because the bound is only a number for some
of the kinds that take one. goingson’s own sites are min="1" on a
duration and min="2026-08-09T14:30" on a datetime, and a numeric member
could say the first and not the second. The kind already
says how to read it, the same way it does for the value.
max: Option<&'a str>The highest value accepted, as the host would write it. See
min.
step: Option<&'a str>The granularity the value moves in, as the host would write it.
Text for min’s reason, and it earns it twice over: the
step of a date is a day and the step of a threshold is 0.01, and a
numeric member could say one of them.
Absent means the host’s own granularity, which is the honest default
rather than a missing value: a webview’s <input> steps by 1 unless told
otherwise, and that is the browser’s rule and not this crate’s to
restate. It matters most to FieldKind::Range, where the host default
turns a 0-to-1 threshold into a two-position control, and it is not
exclusive to it: a stepped Number is the same fact
about a typed value.
Added 0.28.0 with FieldKind::Range.
extended: boolWhether the field lives behind a “more options” disclosure.
Implementations§
Source§impl<'a> Field<'a>
impl<'a> Field<'a>
Sourcepub const fn new(kind: FieldKind, name: &'a str, label: &'a str) -> Self
pub const fn new(kind: FieldKind, name: &'a str, label: &'a str) -> Self
A plain required-nothing field of the given kind.
Sourcepub const fn range(
name: &'a str,
label: &'a str,
min: &'a str,
max: &'a str,
) -> Self
pub const fn range( name: &'a str, label: &'a str, min: &'a str, max: &'a str, ) -> Self
A bounded number the user drags across its whole extent.
The third under-described kind, and it gets a constructor for
select’s reason: a range is the one kind whose bounds
are not a rule but the control itself, so a call site that forgot them
has a slider with nothing to slide across. Taking them as arguments is
what makes that unsayable.
step stays a field rather than a fourth argument. It is
genuinely optional — the host’s granularity is a real answer — and the
two bounds are not.
Sourcepub const fn select(
name: &'a str,
label: &'a str,
options: &'a [Choice<'a>],
) -> Self
pub const fn select( name: &'a str, label: &'a str, options: &'a [Choice<'a>], ) -> Self
A select offering the given options.
One of the two kinds under-described by Field::new, so it gets a
constructor rather than leaving every call site to remember that a
select with an empty options renders as an empty select.
Sourcepub const fn radio(
name: &'a str,
label: &'a str,
options: &'a [Choice<'a>],
) -> Self
pub const fn radio( name: &'a str, label: &'a str, options: &'a [Choice<'a>], ) -> Self
A radio group offering the given options.
The other. Same hazard as select and a worse one: a
radio group with no options draws nothing at all, so a call site that
forgot them has an empty rectangle rather than a visibly empty control.
Sourcepub const fn invalid(&self) -> bool
pub const fn invalid(&self) -> bool
Whether the field is currently reporting a problem.
Read this rather than testing error.is_some() at each renderer: the
error state has to mark the field’s whole group and not only the
message, because a renderer with no descendant selectors (egui, a
terminal) cannot find the group from the message. goingson already marks
the group and Balanced Breakfast does not, so goingson’s shape is the
one taken here.
Sourcepub const fn bounded(&self) -> bool
pub const fn bounded(&self) -> bool
Whether the field carries both ends of its extent.
Only FieldKind::Range owes them, and it owes them absolutely: a
slider with one end missing has no extent to draw. Named here rather
than left to each renderer to test min.is_some() && max.is_some(),
which is three renderers arriving at the same condition and one of them
getting it wrong, and named as a question about the field rather than
about the kind because the kind cannot see the bounds.
It is a check and not a guarantee. Nothing here refuses to build an
unbounded range — Field::range is what makes the bounded one easy —
so a renderer asks this and falls back to whatever its host does
honestly with a number.