I'm attempting to have a trait for things that can either simply contain other things, or create them on demand, given a thing's name. Those contained things should in turn be able to do the same, creating a hierarchy of sorts. Here's a minimal code:
use std::ops::Deref;
pub enum BoxOrRef<'a, T: ?Sized + 'a> {
Boxed(Box<T>),
Ref(&'a T),
}
impl<'a, T: ?Sized + 'a> Deref for BoxOrRef<'a, T> {
type Target = T;
fn deref(&self) -> &T {
match self {
BoxOrRef::Boxed(b) => &b,
BoxOrRef::Ref(r) => r,
}
}
}
pub trait Elem {
fn get_subelem<'a, 'b>(&'a self, name: &'b str) -> Option<BoxOrRef<'a, dyn Elem>>;
}
pub trait Table {
fn get_elem<'a, 'b>(&'a self, name: &'b str) -> Option<BoxOrRef<'a, dyn Elem>>;
}
fn resolve_name<'a, T: Table + ?Sized>(
table: &'a T,
name: &[String],
) -> Option<BoxOrRef<'a, dyn Elem>> {
let mut segments = name.iter();
if let Some(first_segment) = segments.next() {
segments.fold(table.get_elem(&first_segment), |res, next| {
res.and_then(|elem| elem.get_subelem(next))
})
} else {
None
}
}
The lifetime checker however, is not satisfied by this:
error[E0597]: `elem` does not live long enough
--> src/lib.rs:33:33
|
33 | res.and_then(|elem| elem.get_subelem(next))
| ^^^^ - borrowed value only lives until here
| |
| borrowed value does not live long enough
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 26:17...
--> src/lib.rs:26:17
|
26 | fn resolve_name<'a, T: Table + ?Sized>(
| ^^
I need to somehow extend lifetimes of the intermediate res's. I guess I could put them in a struct and tweak the return type of resolve_name to return it along with the final element, but that strikes me as rather clumsy way of doing it. Is there a better solution?
The return value of get_subelem can't outlive the &self borrow you used to call it, because the signature of get_subelem says so explicitly:
fn get_subelem<'a, 'b>(&'a self, name: &'b str) -> Option<BoxOrRef<'a, dyn Elem>>;
// ^^ ^^
In order to get a BoxOrRef<'a, _>, you have to borrow self for the lifetime 'a. In the caller, elem can't outlive the closure it belongs to, and get_subelem borrows elem, so it can't return a value that can escape that closure either.
You're trying to do something that is unsafe, and the compiler is right to stop you. In theory, table.get_elem could return a Boxed value, and elem.get_subelem could return an internal reference, and then the Box would be dropped when the closure returns, invalidating the reference.
Presumably that doesn't actually happen, so you have to tell the compiler that. One way is to decouple &self from BoxOrRef<'a, _>:
pub trait Elem<'a> {
fn get_subelem(&self, name: &str) -> Option<BoxOrRef<'a, dyn Elem<'a>>>;
}
The above change will make your example compile once you add lifetime parameters to all the Elems, but it puts you in an awkward position when implementing Elem: you can't return a reference to self, so practically everything has to be Boxed.
It's hard to make a good recommendation given the vagueness of the example, but I suggest you take a step back and think about whether BoxOrRef is the right abstraction here. Fundamentally, you can't do anything with a BoxOrRef that you couldn't do with a reference, because the BoxOrRef might be a reference. At the same time, you can't do anything with it that you couldn't do with a Box, because it might be a Box. std::borrow::Cow uses ToOwned to implement Clone and into_owned -- perhaps a similar approach could work for you. (And if you can, maybe just implement ToOwned for dyn Elem and use Cow directly.)
Related
I have a trait Atom that has many associated types, of which one is an owned version OP and the other is a borrow version O of essentially the same data. I have a function to_pow_view that creates a view from an owned version and I have an equality operator.
Below is an attempt:
pub trait Atom: PartialEq {
// variants truncated for this example
type P<'a>: Pow<'a, R = Self>;
type OP: OwnedPow<R = Self>;
}
pub trait Pow<'a>: Clone + PartialEq {
type R: Atom;
}
#[derive(Debug, Copy, Clone)]
pub enum AtomView<'a, R: Atom> {
Pow(R::P<'a>),
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum OwnedAtom<R: Atom> {
Pow(R::OP),
}
pub trait OwnedPow {
type R: Atom;
fn some_mutable_fn(&mut self);
fn to_pow_view<'a>(&'a self) -> <Self::R as Atom>::P<'a>;
// compiler said I should add 'a: 'b
fn test<'a: 'b, 'b>(&'a mut self, other: <Self::R as Atom>::P<'b>) {
if self.to_pow_view().eq(&other) {
self.some_mutable_fn();
}
}
}
impl<R: Atom> OwnedAtom<R> {
// compiler said I should add 'a: 'b, why?
pub fn eq<'a: 'b, 'b>(&'a self, other: AtomView<'b, R>) -> bool {
let a: AtomView<'_, R> = match self {
OwnedAtom::Pow(p) => {
let pp = p.to_pow_view();
AtomView::Pow(pp)
}
};
match (&a, &other) {
(AtomView::Pow(l0), AtomView::Pow(r0)) => l0 == r0,
}
}
}
// implementation
#[derive(Debug, Copy, Clone, PartialEq)]
struct Rep {}
impl Atom for Rep {
type P<'a> = PowD<'a>;
type OP = OwnedPowD;
}
#[derive(Debug, Copy, Clone, PartialEq)]
struct PowD<'a> {
data: &'a [u8],
}
impl<'a> Pow<'a> for PowD<'a> {
type R = Rep;
}
struct OwnedPowD {
data: Vec<u8>,
}
impl OwnedPow for OwnedPowD {
type R = Rep;
fn some_mutable_fn(&mut self) {
todo!()
}
fn to_pow_view<'a>(&'a self) -> <Self::R as Atom>::P<'a> {
PowD { data: &self.data }
}
}
fn main() {}
This code gives the error:
27 | fn test<'a: 'b, 'b>(&'a mut self, other: <Self::R as Atom>::P<'b>) {
| -- lifetime `'b` defined here
28 | if self.to_pow_view().eq(&other) {
| ------------------
| |
| immutable borrow occurs here
| argument requires that `*self` is borrowed for `'b`
29 | self.some_mutable_fn();
| ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
I expect this to work, since the immutable borrow should be dropped right after the eq function evaluates.
Something in the setting of the lifetimes is wrong in this code, already in the equality function eq: I would expect that there is no relation between 'a and 'b; they should just live long enough to do the comparison. However, the compiler tells me that I should add 'a: 'b and I do not understand why. The same thing happened for the function test.
These problems lead me to believe that the lifetimes in to_pow_view are wrong, but no modification I tried made it work (except for removing the 'a lifetime on &'a self, but then the OwnedPowD does not compile anymore).
Link to playground
Can someone help understand what is going on?
Here's the point: you constrained Pow to be PartialEq. However, PartialEq is PartialEq<Self>. In other words, Pow<'a> only implements PartialEq<Pow<'a>> for the same 'a.
This is usually the case for any type with lifetime and PartialEq, so why does it always work but not here?
It usually works because if we compare T<'a> == T<'b>, the compiler can shrink the lifetimes to the shortest of the two and compare that.
However, Pow is a trait. Traits are invariant over their lifetime, in other words, it must stay exactly as is, not longer nor shorter. This is because they may be used with invariant types, for example Cell<&'a i32>. Here's an example of how it could be exploited if this was allowed:
use std::cell::Cell;
struct Evil<'a> {
v: Cell<&'a i32>,
}
impl PartialEq for Evil<'_> {
fn eq(&self, other: &Self) -> bool {
// We asserted the lifetimes are the same, so we can do that.
self.v.set(other.v.get());
false
}
}
fn main() {
let foo = Evil { v: Cell::new(&123) };
{
let has_short_lifetime = 456;
_ = foo == Evil { v: Cell::new(&has_short_lifetime) };
}
// Now `foo` contains a dangling reference to `has_short_lifetime`!
dbg!(foo.v.get());
}
The above code does not compile, because Evil is invariant over 'a, but if it would, it would contain UB in safe code. For that reason, traits, that may contain types such as Evil, are also invariant over their lifetimes.
Because of that, the compiler cannot shrink the lifetime of other. It can shrink the lifetime of self.to_pow_view() (in test(), eq() is similar), because it doesn't really shrink it, it just picks a shorter lifetime for to_pow_view()'s self. But because PartialEq is only implemented for types with the same lifetime, it means that the Pow resulting from self.to_pow_view() must have the same lifetime of other. Because of that, (a) 'a must be greater than or equal to 'b, so we can pick 'b out of it, and (b) by comparing, we borrow self for potentially whole 'a, because it may be that 'a == 'b and therefore the comparison borrows self for 'a, so it is still borrowed immutably while we borrow it mutably for some_mutable_fn().
Once we understood the problem, we can think about the solution. Either we require that Pow is covariant over 'a (can be shrinked), or we require that it implements PartialEq<Pow<'b>> for any lifetime 'b. The first is impossible in Rust, but the second is possible:
pub trait Pow<'a>: Clone + for<'b> PartialEq<<Self::R as Atom>::P<'b>> {
type R: Atom;
}
This triggers an error, because the automatically-derived PartialEq does not satisfy this requirement:
error: implementation of `PartialEq` is not general enough
--> src/main.rs:73:10
|
73 | impl<'a> Pow<'a> for PowD<'a> {
| ^^^^^^^ implementation of `PartialEq` is not general enough
|
= note: `PartialEq<PowD<'0>>` would have to be implemented for the type `PowD<'a>`, for any lifetime `'0`...
= note: ...but `PartialEq` is actually implemented for the type `PowD<'1>`, for some specific lifetime `'1`
So we need to implement PartialEq manually:
impl<'a, 'b> PartialEq<PowD<'b>> for PowD<'a> {
fn eq(&self, other: &PowD<'b>) -> bool {
self.data == other.data
}
}
And now it works.
Here, the annotating 'a explicitly for items is required:
struct App<'a> {
items: StatefulList<'a, (&'a str, &'a str, usize)>,
}
impl<'a> App<'a> {
fn new(items: &'a Vec<(&'a str, &'a str, usize)>) -> App<'a> {
App {
items: StatefulList::with_items(items),
}
}
}
However, here it is not:
struct StatefulList<'a, T> {
state: ListState,
items: &'a Vec<T>,
}
impl<'a, T> StatefulList<'a, T> {
fn with_items(items: &Vec<T>) -> StatefulList<T> {
StatefulList {
state: ListState::default(),
items,
}
}
}
Why is that?
For now, I think it is because the compiler can't figure out the lifetime in the first case.
Because of this litte thing:
fn with_items(items: &Vec<T>) -> StatefulList<T> {
// ^ here, where did the lifetime go?
StatefulList is declared with a lifetime. But you omitted the lifetime! What is the lifetime when you omit it?
It is the elided lifetime, '_, or the "figure it out" lifetime. And the compiler "figures it out" via simple liftime elision rules. And part of these rules is that when there is only one lifetime in the parameters (like in this case - the implicit lifetime of the items: &Vec<T>), the lifetimes in the return type all inhreit it. So written explicitly, the signature is:
fn with_items<'b>(items: &'b Vec<T>) -> StatefulList<'b, T>
The 'a lifetime from the impl block is not used at all. The returned StatefulList has the same lifetime as the parameter - which is excellent, because you use the parameter for its items!
In the first case, on the other hand, we explicitly specify the 'a lifetime from the impl block. You still use the parameter, and so the parameter's lifetime has to match - and it has to be 'a too.
There is a lint that will help you avoid that situation: it is called elided_lifetimes_in_paths, and is part of the rust_2018_idioms lints group which I strongly recommend #![forbid]ing for new projects. If you will do that, the compiler will complain:
error: hidden lifetime parameters in types are deprecated
--> src/lib.rs:12:51
|
12 | fn with_items(items: &Vec<T>) -> StatefulList<T> {
| ^ expected named lifetime parameter
|
note: the lint level is defined here
--> src/lib.rs:1:11
|
1 | #![forbid(rust_2018_idioms)]
| ^^^^^^^^^^^^^^^^
= note: `#[forbid(elided_lifetimes_in_paths)]` implied by `#[forbid(rust_2018_idioms)]`
help: consider using the `'_` lifetime
|
12 | fn with_items(items: &Vec<T>) -> StatefulList<'_, T> {
| +++
The thing is that in the second one you do not return anything that has to deal with lifetimes, but an owned version of StatefulList. In the first one it need to match the lifetime to what you are returning, to ensure that the data you return may live enough.
In fact you don't even need some of the annotations, since the compiler will coerce the lifetime for you.
impl<'a> App<'a> {
fn new(items: &'a Vec<(&str, &str, usize)>) -> App<'a> {
App {
items: StatefulList::with_items(items),
}
}
}
&str need to live at least as much as &'a Vec.
Playground
I was writing some code where I want to use an iterator, or its reversed version depending on a flag, but the straightforward code gives an error
pub fn eggs<I,T>(iter:I)->Box<dyn Iterator<Item=T>>
where I:Iterator<Item=T>+DoubleEndedIterator
{
Box::new(iter.rev())
}
pub fn bacon<I,T>(iter:I, reverse:bool) -> Box<dyn Iterator<Item=T>>
where I:Iterator<Item=T>+DoubleEndedIterator
{
if reverse {
Box::new(iter.rev())
} else {
Box::new(iter)
}
}
fn main()
{
let pants:String = "pants".into();
eggs(pants.chars());
}
fails to compile:
error[E0310]: the parameter type `I` may not live long enough
--> src/main.rs:5:5
|
2 | pub fn eggs<I,T>(iter:I)->Box<dyn Iterator<Item=T>>
| - help: consider adding an explicit lifetime bound...: `I: 'static`
...
5 | Box::new(iter.rev())
| ^^^^^^^^^^^^^^^^^^^^ ...so that the type `Rev<I>` will meet its required lifetime bounds
With my limited understanding of Rust, I'm not sure where those lifetime bounds are coming from. There aren't any on the Iterator trait, or the Rev struct, and the parameter is being moved.
What is the proper way to declare these sorts of functions given that 'static isn't really an option.
rust playground
This doesn't have to do with .rev() at all, but with returning Box<dyn Iterator>:
// error[E0310]: the parameter type `I` may not live long enough
fn boxed_iter<I, T>(iter: I) -> Box<dyn Iterator<Item = T>>
// - help: consider adding an explicit lifetime bound...: `I: 'static`
where
I: Iterator<Item = T>,
{
Box::new(iter)
// ^^^^^^^^^^^^^^ ...so that the type `I` will meet its required lifetime bounds
}
The reason for this is that trait objects like Box<dyn Trait> have an implicit 'static lifetime if not specified. So when the compiler tries to cast Box<I> to Box<dyn Iterator>, it fails if I is does not also have a 'static lifetime. (There are some more specific rules if the trait contains lifetimes itself; you can read about those in more detail here.)
If you instead want a shorter lifetime, you need to specify it explicitly as Box<dyn 'a + Trait>. So for example:
fn boxed_iter<'a, I, T>(iter: I) -> Box<dyn 'a + Iterator<Item = T>>
where
I: 'a + Iterator<Item = T>,
{
Box::new(iter)
}
Frxstrem's answer is excellent. I just want to add that, if you know that the return value of your function has a specific concrete type, you can use the special impl trait syntax.
In the case of your eggs function, the return type is probably something like Rev<I>. That type, on its own, isn't very illuminating, but we know that such a type exists and the only thing we care about is that it's an iterator, so we can write
pub fn eggs<I,T>(iter:I) -> impl Iterator<Item=T> + DoubleEndedIterator
where I: Iterator<Item=T> + DoubleEndedIterator {
iter.rev()
}
Now the compiler still understands that there is a single concrete type and will act accordingly (no need to box the value or have dynamic dispatch), but we as the programmers still only have to care about the Iterator and DoubleEndedIterator aspects of it. Zero-cost abstraction at its finest.
Your bacon function can't benefit from this, as it could return either an I or a Rev<I> depending on input, so the dynamic dispatch is actually necessary. In that case, you'll need to follow Frxstrem's answer to correctly box your iterator.
I have a History type which contains an initial state, a current state, and a list of closures with every change required to compute the current state from the initial state. These are applied with an .apply(...) method which takes a boxed closure, uses it to modify the current state, and adds it to the list. Because I want these to be deterministically reusable they are Fn, not FnMut or FnOnce.
struct History<State: Clone> {
initial: State,
current: State,
updates: Vec<Box<dyn Fn(&mut State)>>,
}
impl<State: Clone> History<State> {
fn apply(&mut self, update: Box<dyn Fn(&mut State)>) {
update(&mut self.current);
self.updates.push(update);
}
}
I am currently taking closures as Box<dyn Fn(&mut State)>, and it works fine:
fn main() {
let mut history = History::<usize> {
initial: 0,
current: 0,
updates: vec![],
};
let delta = 10;
history.apply(Box::new(move |mut value| *value += delta));
println!("{}", history.current);
}
10
This got me thinking about whether it would be possible for a method to accept arbitrary unboxed closures by using impl Trait instead of dyn Trait. In this case, our method could box the closures itself, so the call site would become:
history.apply(move |mut value| *value += delta);
(Please entertain the question of whether this is even possible, even if it's a bad idea in this case.)
I am imagining that each closure site is like a distinct data type, instantiated with the enclosed values each time it's used, so impl Trait could specialize the method for each implicit closure like it does for each explicit type. But I'm not sure whether Rust actually works like that.
However, when I try making the change in the code, I get a new lifetime error:
fn apply(&mut self, update: impl Fn(&mut State)) {
update(&mut self.current);
self.updates.push(Box::new(update));
}
error[E0310]: the parameter type `impl Fn(&mut State)` may not live long enough
--> src/main.rs:10:27
|
10 | self.updates.push(Box::new(update));
| ^^^^^^^^^^^^^^^^
|
note: ...so that the type `impl Fn(&mut State)` will meet its required lifetime bounds
--> src/main.rs:10:27
|
10 | self.updates.push(Box::new(update));
| ^^^^^^^^^^^^^^^^
This confuses me. I'm not sure where there's a reference that could be going bad.
In my head, the entire closure state is now being moved into apply via the impl Fn parameter, then moved into a Box that is owned by self. But it's complaining that I can't move the contents to a box, because I have a potentially-stale reference, not just owned data? Where am I borrowing anything? Why didn't this occur when I boxed the closure directly in main instead of in apply?
Is it possible to use impl Fn to accept an (arbitrarily-sized) closure as an argument? If so, how?
Can you use impl Fn to accept arbitrarily-sized closures as arguments?
Yes.
impl trait in argument position is the exact same as a generic. These are identical:
fn foo1(_: impl Fn(u8) -> i8) {}
fn foo2<F>(_: F)
where
F: Fn(u8) -> i8,
{}
This is in fact the generally preferred way to accept a closure (or many other trait implementations) because it allows the compiler to monomorphize the result and avoids any unneeded indirection.
Compiling your code has this help text (which currently has some rendering glitches):
help: consider adding an explicit lifetime bound `impl Fn(&mut State): 'static`...
|
8 | fn apply(&mut self, update: impl Fn(&mut State): 'static + {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
What it means to say is to add + 'static:
fn apply(&mut self, update: impl Fn(&mut State) + 'static)
This works.
See also:
What makes `impl Trait` as an argument "universal" and as a return value "existential"?
Why is adding a lifetime to a trait with the plus operator (Iterator<Item = &Foo> + 'a) needed?
The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want
Can anyone tell what the problem is with the following code? The compiler is complaining about lifetimes, but the error message makes absolutely no sense. I've tried everything I could think of, but nothing seems to help.
use std::borrow::BorrowMut;
trait Trait<'a> {
fn accept(&mut self, &'a u8);
}
struct Impl<'a>{
myref: Option<&'a u8>,
}
impl<'a> Trait<'a> for Impl<'a> {
fn accept(&mut self, inp: &'a u8) { self.myref = Some(inp); }
}
fn new<'a>() -> Box<Trait<'a> + 'a> {
Box::new(Impl{myref: None})
}
fn user<'a>(obj: &mut Trait<'a>) {}
fn parent<'a>(x: &'a u8) {
let mut pool = new();
user(pool.borrow_mut());
}
The compiler error is
error: `pool` does not live long enough
--> src/wtf.rs:22:10
|
22 | user(pool.borrow_mut());
| ^^^^ does not live long enough
23 | }
| - borrowed value dropped before borrower
|
= note: values in a scope are dropped in the opposite order they are created
Which makes absolutely no sense. How is the borrower outliving anything? I'm not even using the borrowed value!
Ok, this does make sense, but it's hard to see due to lifetime elision. So, here's your code with all the lifetimes written out explicitly, and with irrelevant details culled:
use std::borrow::BorrowMut;
trait Trait<'a> {}
struct Impl<'a> {
myref: Option<&'a u8>,
}
impl<'a> Trait<'a> for Impl<'a> {}
fn new<'a>() -> Box<Trait<'a> + 'a> {
Box::new(Impl { myref: None })
}
fn user<'a, 'b>(obj: &'b mut (Trait<'a> + 'b)) {}
fn parent() {
/* 'i: */ let mut pool/*: Box<Trait<'x> + 'x>*/ = new();
/* 'j: */ let pool_ref/*: &'i mut Box<Trait<'x> + 'x>*/ = &mut pool;
/* BorrowMut<T>::borrow_mut<'d>(&'d mut Self) -> &'d mut T */
/* 'k: */ let pool_borrow/*: &'i mut (Trait<'x> + 'x)*/ = Box::borrow_mut(pool_ref);
user(pool_borrow);
}
Now, from the perspective of the last line of parent, we can work out the following equivalences by just reading the definition of user and substituting the lifetimes we have in parent:
'a = 'x
'b = 'i
'b = 'x
Furthermore, this lets us conclude that:
'x = 'i
This is the problem. Because of the way you've defined user, you've put yourself in a situation where the lifetime of the pool_ref borrow (which is equal to the lifetime of the pool storage location you're borrowing from) must be the same as the lifetime 'x being used in the thing being stored in pool.
It's a bit like the Box being able to have a pointer to itself before it exists, which doesn't make any sense.
Either way, the fix is simple. Change user to actually have the correct type:
fn user<'a, 'b>(obj: &'b mut (Trait<'a> + 'a)) {}
This matches the type produced by new. Alternately, just don't use borrow_mut:
user(&mut *pool)
This works because it is "re-borrowing". Calling borrow_mut translates the lifetimes more or less directly, but re-borrowing allows the compiler to narrow the borrows to shorter lifetimes. To put it another way, explicitly calling borrow_mut doesn't allow the compiler enough freedom to "fudge" the lifetimes to make them all line up, re-borrowing does.
As a quick aside:
I'm not even using the borrowed value!
Irrelevant. Rust does type- and lifetime-checking entirely locally. It never looks at the body of another function to see what it's doing; it goes on the interface alone. The compiler neither checks, nor cares, what you're doing inside a different function.
Note that there's more to the error message:
error: `pool` does not live long enough
--> src/main.rs:25:10
|>
25 |> user(pool.borrow_mut());
|> ^^^^
note: reference must be valid for the block at 23:25...
--> src/main.rs:23:26
|>
23 |> fn parent<'a>(x: &'a u8) {
|> ^
note: ...but borrowed value is only valid for the block suffix following statement 0 at 24:25
--> src/main.rs:24:26
|>
24 |> let mut pool = new();
|> ^
Let's look at user:
fn user<'a>(obj: &mut Trait<'a>) {}
This says that it will accept a mutable reference (with an unnamed lifetime) to a trait object parameterized with the lifetime 'a.
Turning to new, I'd say the method is highly suspicious:
fn new<'a>() -> Box<Trait<'a> + 'a> {
Box::new(Impl { myref: None })
}
This says that it will return a boxed trait object with whatever lifetime the caller specifies. That basically never makes sense.
All that said, I'm not clear why the code chooses to use borrow_mut. I would have written that more directly:
user(&mut *pool);
This dereferences the Box<Trait> to get a Trait, then takes a mutable reference, yielding &mut Trait, which compiles.
I cannot currently explain why BorrowMut differs in behavior.
I'm not sure why this error happens, but I can give solutions!
First, it seems that using borrow_mut unnecessarily restricts the lifetime of the returned reference. Using operators to create the reference solves the error.
fn parent() {
let mut pool = new();
user(&mut *pool);
}
However, if we don't do that, we can solve the error by adding a lifetime bound to the Trait object in user's obj argument.
fn user<'a>(obj: &mut (Trait<'a> + 'a)) {}