Skip to content

Commit 685b345

Browse files
committed
Remove fancy lifetime stuff from Env
Previously, we were doing a bunch of gymnastics in order to allow theEnv to return either borrowed or owned data, depending on the type; the borrowed case was only used for Strings. This code was brittle and hard to understand, and ultimately I think it was solving the wrong problem: instead of trying to avoid cloning expensive data when we get things out of the map, I would prefer to just not have expensive-to-clone things in there in the first place. This introduces the ArcStr type alias for Arc<str>, which is my preferred type for simple strings in druid. Realistically we should have a conversation soon about string types, since we're almost certainly going to be bringing in the xi Rope structure to represent editable text, and we may just want to use that for everything; in that case it will be easy enough to just replace ArcStr with Rope next week. This opens up a bunch of possible future work. The problem I'm most focused on right now is how to synthesize values out of the env; this is sort of like 'env lensing', and it wasn't possible because of the lifetimes. I also think that we should consider removing the `Data` impl from String, but I want to wait until we have a clearer sense of what our text types end up being.
1 parent a8fb9ca commit 685b345

File tree

9 files changed

+58
-83
lines changed

9 files changed

+58
-83
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ You can find its changes [documented below](#060---2020-06-01).
3030
- Use new Piet text api ([#1143] by [@cmyr])
3131
- `Env::try_get` (and related methods) return a `Result` instead of an `Option`. ([#1172] by [@cmyr])
3232
- `lens!` macro to use move semantics for the index. ([#1171] by [@finnerale])
33+
- `Env` stores `Arc<str>` instead of `String` ([#1173] by [@cmyr])
3334

3435
### Deprecated
3536

@@ -408,6 +409,7 @@ Last release without a changelog :(
408409
[#1157]: https://github.com/linebender/druid/pull/1157
409410
[#1171]: https://github.com/linebender/druid/pull/1171
410411
[#1172]: https://github.com/linebender/druid/pull/1172
412+
[#1173]: https://github.com/linebender/druid/pull/1173
411413

412414
[Unreleased]: https://github.com/linebender/druid/compare/v0.6.0...master
413415
[0.6.0]: https://github.com/linebender/druid/compare/v0.5.0...v0.6.0

druid/examples/styled_text.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616
1717
use druid::widget::{Checkbox, Flex, Label, MainAxisAlignment, Painter, Parse, Stepper, TextBox};
1818
use druid::{
19-
theme, AppLauncher, Color, Data, Key, Lens, LensExt, LensWrap, LocalizedString, PlatformError,
20-
RenderContext, Widget, WidgetExt, WindowDesc,
19+
theme, AppLauncher, ArcStr, Color, Data, Key, Lens, LensExt, LensWrap, LocalizedString,
20+
PlatformError, RenderContext, Widget, WidgetExt, WindowDesc,
2121
};
2222
use std::fmt::Display;
2323

2424
// This is a custom key we'll use with Env to set and get our text size.
2525
const MY_CUSTOM_TEXT_SIZE: Key<f64> = Key::new("styled_text.custom_text_size");
26-
const MY_CUSTOM_FONT: Key<&str> = Key::new("styled_text.custom_font");
26+
const MY_CUSTOM_FONT: Key<ArcStr> = Key::new("styled_text.custom_font");
2727

2828
#[derive(Clone, Lens, Data)]
2929
struct AppData {
@@ -99,7 +99,7 @@ fn ui_builder() -> impl Widget<AppData> {
9999
if data.mono {
100100
env.set(MY_CUSTOM_FONT, "monospace");
101101
} else {
102-
env.set(MY_CUSTOM_FONT, env.get(theme::FONT_NAME).to_string());
102+
env.set(MY_CUSTOM_FONT, env.get(theme::FONT_NAME));
103103
}
104104
});
105105

druid/src/data.rs

+7
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ pub trait Data: Clone + 'static {
119119
//// ANCHOR_END: same_fn
120120
}
121121

122+
/// A reference counted string slice.
123+
///
124+
/// This is a data-friendly way to represent strings in druid. Unlike `String`
125+
/// it cannot be mutated, but unlike `String` it can be cheaply cloned.
126+
pub type ArcStr = Arc<str>;
127+
122128
/// An impl of `Data` suitable for simple types.
123129
///
124130
/// The `same` method is implemented with equality, so the type should
@@ -145,6 +151,7 @@ impl_data_simple!(u64);
145151
impl_data_simple!(usize);
146152
impl_data_simple!(char);
147153
impl_data_simple!(bool);
154+
//TODO: remove me!?
148155
impl_data_simple!(String);
149156

150157
impl Data for f32 {

druid/src/env.rs

+35-69
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use std::ops::Deref;
2323
use std::sync::Arc;
2424

2525
use crate::localization::L10nManager;
26-
use crate::{Color, Data, Point, Rect, Size};
26+
use crate::{ArcStr, Color, Data, Point, Rect, Size};
2727

2828
/// An environment passed down through all widget traversals.
2929
///
@@ -52,7 +52,7 @@ pub struct Env(Arc<EnvImpl>);
5252

5353
#[derive(Clone)]
5454
struct EnvImpl {
55-
map: HashMap<String, Value>,
55+
map: HashMap<ArcStr, Value>,
5656
debug_colors: Vec<Color>,
5757
l10n: Arc<L10nManager>,
5858
}
@@ -107,7 +107,7 @@ pub enum Value {
107107
Float(f64),
108108
Bool(bool),
109109
UnsignedInt(u64),
110-
String(String),
110+
String(ArcStr),
111111
}
112112
// ANCHOR_END: value_type
113113

@@ -132,17 +132,9 @@ pub enum KeyOrValue<T> {
132132
}
133133

134134
/// Values which can be stored in an environment.
135-
///
136-
/// Note that for "expensive" types this is the reference. For example,
137-
/// for strings, this trait is implemented on `&'a str`. The trait is
138-
/// parametrized on a lifetime so that it can be used for references in
139-
/// this way.
140-
pub trait ValueType<'a>: Sized {
141-
/// The corresponding owned type.
142-
type Owned: Into<Value>;
143-
135+
pub trait ValueType: Sized + Into<Value> {
144136
/// Attempt to convert the generic `Value` into this type.
145-
fn try_from_value(v: &'a Value) -> Result<Self, ValueTypeError>;
137+
fn try_from_value(v: &Value) -> Result<Self, ValueTypeError>;
146138
}
147139

148140
/// The error type for environment access.
@@ -214,7 +206,7 @@ impl Env {
214206
/// # Panics
215207
///
216208
/// Panics if the key is not found, or if it is present with the wrong type.
217-
pub fn get<'a, V: ValueType<'a>>(&'a self, key: impl Borrow<Key<V>>) -> V {
209+
pub fn get<V: ValueType>(&self, key: impl Borrow<Key<V>>) -> V {
218210
match self.try_get(key) {
219211
Ok(value) => value,
220212
Err(err) => panic!("{}", err),
@@ -228,10 +220,7 @@ impl Env {
228220
/// # Panics
229221
///
230222
/// Panics if the value for the key is found, but has the wrong type.
231-
pub fn try_get<'a, V: ValueType<'a>>(
232-
&'a self,
233-
key: impl Borrow<Key<V>>,
234-
) -> Result<V, MissingKeyError> {
223+
pub fn try_get<V: ValueType>(&self, key: impl Borrow<Key<V>>) -> Result<V, MissingKeyError> {
235224
self.0
236225
.map
237226
.get(key.borrow().key)
@@ -277,12 +266,12 @@ impl Env {
277266
///
278267
/// *WARNING:* This is not intended for general use, but only for inspecting an `Env` e.g.
279268
/// for debugging, theme editing, and theme loading.
280-
pub fn get_all(&self) -> impl ExactSizeIterator<Item = (&String, &Value)> {
269+
pub fn get_all(&self) -> impl ExactSizeIterator<Item = (&ArcStr, &Value)> {
281270
self.0.map.iter()
282271
}
283272

284273
/// Adds a key/value, acting like a builder.
285-
pub fn adding<'a, V: ValueType<'a>>(mut self, key: Key<V>, value: impl Into<V::Owned>) -> Env {
274+
pub fn adding<V: ValueType>(mut self, key: Key<V>, value: impl Into<V>) -> Env {
286275
let env = Arc::make_mut(&mut self.0);
287276
env.map.insert(key.into(), value.into().into());
288277
self
@@ -294,7 +283,7 @@ impl Env {
294283
///
295284
/// Panics if the environment already has a value for the key, but it is
296285
/// of a different type.
297-
pub fn set<'a, V: ValueType<'a>>(&'a mut self, key: Key<V>, value: impl Into<V::Owned>) {
286+
pub fn set<V: ValueType>(&mut self, key: Key<V>, value: impl Into<V>) {
298287
let env = Arc::make_mut(&mut self.0);
299288
let value = value.into().into();
300289
let key = key.into();
@@ -369,7 +358,7 @@ impl Value {
369358
/// # Panics
370359
///
371360
/// Panics when the value variant doesn't match the provided type.
372-
pub fn to_inner_unchecked<'a, V: ValueType<'a>>(&'a self) -> V {
361+
pub fn to_inner_unchecked<V: ValueType>(&self) -> V {
373362
match ValueType::try_from_value(self) {
374363
Ok(v) => v,
375364
Err(s) => panic!("{}", s),
@@ -420,7 +409,7 @@ impl Data for Value {
420409
(Float(f1), Float(f2)) => f1.same(&f2),
421410
(Bool(b1), Bool(b2)) => b1 == b2,
422411
(UnsignedInt(f1), UnsignedInt(f2)) => f1.same(&f2),
423-
(String(s1), String(s2)) => s1 == s2,
412+
(String(s1), String(s2)) => s1.same(s2),
424413
_ => false,
425414
}
426415
}
@@ -482,9 +471,9 @@ impl Default for Env {
482471
}
483472
}
484473

485-
impl<T> From<Key<T>> for String {
486-
fn from(src: Key<T>) -> String {
487-
String::from(src.key)
474+
impl<T> From<Key<T>> for ArcStr {
475+
fn from(src: Key<T>) -> ArcStr {
476+
ArcStr::from(src.key)
488477
}
489478
}
490479

@@ -520,10 +509,9 @@ impl std::error::Error for ValueTypeError {}
520509
impl std::error::Error for MissingKeyError {}
521510

522511
/// Use this macro for types which are cheap to clone (ie all `Copy` types).
523-
macro_rules! impl_value_type_owned {
512+
macro_rules! impl_value_type {
524513
($ty:ty, $var:ident) => {
525-
impl<'a> ValueType<'a> for $ty {
526-
type Owned = $ty;
514+
impl ValueType for $ty {
527515
fn try_from_value(value: &Value) -> Result<Self, ValueTypeError> {
528516
match value {
529517
Value::$var(f) => Ok(f.to_owned()),
@@ -540,57 +528,35 @@ macro_rules! impl_value_type_owned {
540528
};
541529
}
542530

543-
/// Use this macro for types which require allocation but are not too
544-
/// expensive to clone.
545-
macro_rules! impl_value_type_borrowed {
546-
($ty:ty, $owned:ty, $var:ident) => {
547-
impl<'a> ValueType<'a> for &'a $ty {
548-
type Owned = $owned;
549-
fn try_from_value(value: &'a Value) -> Result<Self, ValueTypeError> {
550-
match value {
551-
Value::$var(f) => Ok(f),
552-
other => Err(ValueTypeError::new(any::type_name::<$ty>(), other.clone())),
553-
}
554-
}
555-
}
556-
557-
impl Into<Value> for $owned {
558-
fn into(self) -> Value {
559-
Value::$var(self)
560-
}
561-
}
562-
};
563-
}
564-
565-
impl_value_type_owned!(f64, Float);
566-
impl_value_type_owned!(bool, Bool);
567-
impl_value_type_owned!(u64, UnsignedInt);
568-
impl_value_type_owned!(Color, Color);
569-
impl_value_type_owned!(Rect, Rect);
570-
impl_value_type_owned!(Point, Point);
571-
impl_value_type_owned!(Size, Size);
572-
impl_value_type_borrowed!(str, String, String);
531+
impl_value_type!(f64, Float);
532+
impl_value_type!(bool, Bool);
533+
impl_value_type!(u64, UnsignedInt);
534+
impl_value_type!(Color, Color);
535+
impl_value_type!(Rect, Rect);
536+
impl_value_type!(Point, Point);
537+
impl_value_type!(Size, Size);
538+
impl_value_type!(ArcStr, String);
573539

574-
impl<'a, T: ValueType<'a>> KeyOrValue<T> {
540+
impl<T: ValueType> KeyOrValue<T> {
575541
/// Resolve the concrete type `T` from this `KeyOrValue`, using the provided
576542
/// [`Env`] if required.
577543
///
578544
/// [`Env`]: struct.Env.html
579-
pub fn resolve(&'a self, env: &'a Env) -> T {
545+
pub fn resolve(&self, env: &Env) -> T {
580546
match self {
581547
KeyOrValue::Concrete(value) => value.to_inner_unchecked(),
582548
KeyOrValue::Key(key) => env.get(key),
583549
}
584550
}
585551
}
586552

587-
impl<'a, V: Into<Value>, T: ValueType<'a, Owned = V>> From<V> for KeyOrValue<T> {
588-
fn from(value: V) -> KeyOrValue<T> {
553+
impl<T: Into<Value>> From<T> for KeyOrValue<T> {
554+
fn from(value: T) -> KeyOrValue<T> {
589555
KeyOrValue::Concrete(value.into())
590556
}
591557
}
592558

593-
impl<'a, T: ValueType<'a>> From<Key<T>> for KeyOrValue<T> {
559+
impl<T: ValueType> From<Key<T>> for KeyOrValue<T> {
594560
fn from(key: Key<T>) -> KeyOrValue<T> {
595561
KeyOrValue::Key(key)
596562
}
@@ -602,12 +568,12 @@ mod tests {
602568

603569
#[test]
604570
fn string_key_or_value() {
605-
const MY_KEY: Key<&str> = Key::new("test.my-string-key");
606-
let env = Env::default().adding(MY_KEY, "Owned".to_string());
607-
assert_eq!(env.get(MY_KEY), "Owned");
571+
const MY_KEY: Key<ArcStr> = Key::new("test.my-string-key");
572+
let env = Env::default().adding(MY_KEY, "Owned");
573+
assert_eq!(env.get(MY_KEY).as_ref(), "Owned");
608574

609-
let key: KeyOrValue<&str> = MY_KEY.into();
610-
let value: KeyOrValue<&str> = "Owned".to_string().into();
575+
let key: KeyOrValue<ArcStr> = MY_KEY.into();
576+
let value: KeyOrValue<ArcStr> = ArcStr::from("Owned").into();
611577

612578
assert_eq!(key.resolve(&env), value.resolve(&env));
613579
}

druid/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ pub use app_delegate::{AppDelegate, DelegateCtx};
185185
pub use box_constraints::BoxConstraints;
186186
pub use command::{sys as commands, Command, Selector, SingleUse, Target};
187187
pub use contexts::{EventCtx, LayoutCtx, LifeCycleCtx, PaintCtx, UpdateCtx};
188-
pub use data::Data;
188+
pub use data::{ArcStr, Data};
189189
pub use env::{Env, Key, KeyOrValue, Value, ValueType};
190190
pub use event::{Event, InternalEvent, InternalLifeCycle, LifeCycle};
191191
pub use ext_event::{ExtEventError, ExtEventSink};

druid/src/theme.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#![allow(missing_docs)]
1818
use crate::piet::Color;
1919

20-
use crate::{Env, Key};
20+
use crate::{ArcStr, Env, Key};
2121

2222
pub const WINDOW_BACKGROUND_COLOR: Key<Color> = Key::new("window_background_color");
2323

@@ -41,7 +41,7 @@ pub const SELECTION_COLOR: Key<Color> = Key::new("selection_color");
4141
pub const SELECTION_TEXT_COLOR: Key<Color> = Key::new("selection_text_color");
4242
pub const CURSOR_COLOR: Key<Color> = Key::new("cursor_color");
4343

44-
pub const FONT_NAME: Key<&str> = Key::new("font_name");
44+
pub const FONT_NAME: Key<ArcStr> = Key::new("font_name");
4545
pub const TEXT_SIZE_NORMAL: Key<f64> = Key::new("text_size_normal");
4646
pub const TEXT_SIZE_LARGE: Key<f64> = Key::new("text_size_large");
4747
pub const BASIC_WIDGET_HEIGHT: Key<f64> = Key::new("basic_widget_height");

druid/src/widget/label.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::piet::{
1919
TextLayoutBuilder, UnitPoint,
2020
};
2121
use crate::{
22-
theme, BoxConstraints, Data, Env, Event, EventCtx, KeyOrValue, LayoutCtx, LifeCycle,
22+
theme, ArcStr, BoxConstraints, Data, Env, Event, EventCtx, KeyOrValue, LayoutCtx, LifeCycle,
2323
LifeCycleCtx, LocalizedString, PaintCtx, Point, Size, UpdateCtx, Widget,
2424
};
2525

@@ -55,7 +55,7 @@ pub struct Label<T> {
5555
text: LabelText<T>,
5656
color: KeyOrValue<Color>,
5757
size: KeyOrValue<f64>,
58-
font: KeyOrValue<&'static str>,
58+
font: KeyOrValue<ArcStr>,
5959
}
6060

6161
impl<T: Data> Label<T> {
@@ -141,7 +141,7 @@ impl<T: Data> Label<T> {
141141
/// The argument can be a `&str`, `String`, or [`Key<&str>`].
142142
///
143143
/// [`Key<&str>`]: ../struct.Key.html
144-
pub fn with_font(mut self, font: impl Into<KeyOrValue<&'static str>>) -> Self {
144+
pub fn with_font(mut self, font: impl Into<KeyOrValue<ArcStr>>) -> Self {
145145
self.font = font.into();
146146
self
147147
}
@@ -187,7 +187,7 @@ impl<T: Data> Label<T> {
187187
/// The argument can be a `&str`, `String`, or [`Key<&str>`].
188188
///
189189
/// [`Key<&str>`]: ../struct.Key.html
190-
pub fn set_font(&mut self, font: impl Into<KeyOrValue<&'static str>>) {
190+
pub fn set_font(&mut self, font: impl Into<KeyOrValue<ArcStr>>) {
191191
self.font = font.into();
192192
}
193193

@@ -198,7 +198,7 @@ impl<T: Data> Label<T> {
198198

199199
// TODO: caching of both the format and the layout
200200
self.text.with_display_text(|text| {
201-
let font = t.font_family(font_name).unwrap_or(FontFamily::SYSTEM_UI);
201+
let font = t.font_family(&font_name).unwrap_or(FontFamily::SYSTEM_UI);
202202
t.new_text_layout(&text)
203203
.font(font, font_size)
204204
.text_color(color.clone())

druid/src/widget/switch.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ impl Switch {
5959

6060
let font = ctx
6161
.text()
62-
.font_family(font_name)
62+
.font_family(&font_name)
6363
.unwrap_or(FontFamily::SYSTEM_UI);
6464

6565
// off/on labels

druid/src/widget/textbox.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ impl TextBox {
100100

101101
// TODO: caching of both the format and the layout
102102
let font = piet_text
103-
.font_family(font_name)
103+
.font_family(&font_name)
104104
.unwrap_or(FontFamily::SYSTEM_UI);
105105

106106
piet_text

0 commit comments

Comments
 (0)