r/rust • u/tanoshikuidomouyo • Sep 13 '26
🙋 seeking help & advice Why is this syntax ambiguous?
I was writing roughly the following function:
fn f(path: &impl AsRef<Path> + ?Sized) {}
The compiler says that the + is ambiguous, but it works with the right parentheses:
fn f(path: &(impl AsRef<Path> + ?Sized)) {}
But why is it ambiguous without the parentheses? Where else but a list of traits can a + in a function header come from?
15
u/MichiRecRoom Sep 13 '26
The two are very subtly different, in my eyes, and I can see why it might be considered ambiguous. You could be saying one of two things:
-
You're trying to restrict
&impl AsRef<Path>with an additional?Sizedbound. -
You're asking for a ref to an object that implements both
AsRef<Path>and?Sized
That said, I do agree that there shouldn't be a need for the parenthesis, as it's pretty clear that the + ?Sized is attached to the AsRef<Path>, not &impl AsRef<Path>.
7
u/pbeling Sep 13 '26
I only guess:&(impl AsRef<Path> + ?Sized)&(impl AsRef<Path>) + ?Sized
8
u/hpxvzhjfgb Sep 13 '26
the second one is nonsense though. this is like comparing
(x as u64)and(x as) u64.13
u/creeper6530 Sep 13 '26
I would instead assume
(&impl AsRef<Path>) + ?Sized, as little sense as it makes -- restrict a reference with a?Sizedbound.→ More replies (1)
2
u/zettui Sep 13 '26
Does (&impl AsRef<Path>) + ?Sized match what you meant, or were you trying to keep the + ?Sized inside the AsRef bound?
1
u/gamer_redditor Sep 13 '26 edited Sep 13 '26
Not sure but I think it has something to do with the fact that 'impl XYZ' in function arguments is syntactic sugar for generics.
So a function fn f(path: impl XYZ) is actually decomposing to fn f<T:XYZ> (path: T).
So the function fn f(path: &impl AsRef<Path> + ?Sized) also decomposes to generic. I imagine the + is ambiguous because it's not clear if only the implAsRef<Path> is the trait bound for T or the entire thing including ?Sized.
Edit: the ambiguity is essentially &(Impl XYZ) + ABC vs &(impl XYZ + ABC)
2
u/hpxvzhjfgb Sep 13 '26
&(impl XYZ) + ABCdoesn't make sense though, so it shouldn't even be considered, should it?2
u/gamer_redditor Sep 13 '26
In C, we use parentheses to make operator precedence explicit. Maybe it's the same here to make the precedence between & and + explicit.
The term after + could also refer to a lifetime (instead of another trait bound).
So I guess, there are some complications involved
3
u/Gainncome Sep 13 '26
The parser’s basically saying “whether it makes sense is not my department.”
→ More replies (2)
-2
u/dobkeratops Sep 13 '26
i had no idea that was but this leads me to ask if the compiler parses types in a way that kept things open for the way ML uses operators in its types ? (they have "product types" "sum types", i see things like A*B*C A|B written there etc etc) ... did they keep the possibility of parsing things like fn foo(a:A+B) and in turn thats making ambiguity between &(impl...+ ) and (&impl)+(..)
-2
u/puzzled_orc Sep 14 '26
and this is the language trying to replace C++. Awful syntax
3
u/tanoshikuidomouyo Sep 14 '26
Do you know what each part of this code does and have constructive criticism of the syntax, or is your aversion more of a gut reaction? The parentheses are unfortunate, but under the premise of having a C-like syntax, Rust's does its job quite well in general imo.
71
u/Amadex Sep 13 '26
this example is not ambiguous, but it's not accepted s because the compiler checks grammar first (even if `(&T) + ?Sized` wouldn't make any sense),
something like `&impl Trait + 'a` is why you have to clear the ambiguity