Conversation
|
I'm not necessarily against the RFC, but the motivation and the RFC's change seem completely separate. I don't understand how "people have to import too many things to make serious projects" leads to "and now |
In crates like use windows::{
core::*, Data::Xml::Dom::*, Win32::Foundation::*, Win32::System::Threading::*,
Win32::UI::WindowsAndMessaging::*,
}; |
|
Even assuming I agreed that's bad practice (which, I don't), it is not clear how that motivation has lead to this proposed change. |
How can I make this RFC more convincing? I am really new to this and seeing as you are a contributor I would like to ask for your help. |
|
First, I'm not actually on any team officially, so please don't take my comments with too much weight. That said:
Here's my question: Is your thinking that an expansion of inference will let people import less types, and then that would cause them to use glob imports less? Assuming yes, well this inference change wouldn't make me glob import less. I like the glob imports. I want to write it once and just "make the compiler stop bugging me" about something that frankly always feels unimportant. I know it's obviously not actually unimportant but it feels unimportant to stop and tell the compiler silly details over and over. Even if the user doesn't have to import as many types they still have to import all the functions, so if we're assuming that "too many imports" is the problem and that reducing the number below some unknown threshold will make people not use glob imports, I'm not sure this change reduces the number of imports below that magic threshold. Because for me the threshold can be as low as two items. If I'm adding a second item from the same module and I think I might ever want a third from the same place I'll just make it a glob. Is the problem with glob imports that they're not explicit enough about where things come from? Because if the type of I hope this isn't too harsh all at once, and I think more inference might be good, but I'm just not clear what your line of reasoning is about how the problem leads to this specific solution. |
Part of it yes, but, I sometimes get really frustrated that I keep having to specify types and that simple things like match statements require me to sepcigy the type every single time.
Its imported in the background. Although we don't need the exact path, the compiler knows and it can be listed in the rust doc.
Definitely not, you point out some great points and your constructive feedback is welcome. |
|
Personally |
|
I would like to suggest an alternative rigorous definition that satisfies the examples mentioned in the RFC (although not very intuitive imo): When one of the following expression forms (set A) is encountered as the top-level expression in the following positions (set B), the Set A:
Set B:
Set B only applies when the type of the expression at the position can be inferred without resolving the expression itself. Note that this definition explicitly states that Set B does not involve macros. Whether this works for macros like Set A is a pretty arbitrary list for things that typically seem to want the expected type. We aren't really inferring anything in set A, just blind expansion based on the inference from set B. These lists will need to be constantly maintained and updated when new expression types/positions appear. |
That is so useful! Let me fix it now. |
|
One interesting quirk to think about (although unlikely): fn foo<T: Default>(t: T) {}
foo(_::default())should this be allowed? we are not dealing with type inference here, but more like "trait inference". |
I think you would have to specify the type arg on this one because fn foo<T: Default>(t: T) {}
foo::<StructImplementingDefault>(_::default()) |
|
oh never mind, right, we don't really need to reference the trait directly either way. |
|
I've been putting off reading this RFC, and looking at the latest version, I can definitely feel like once the aesthetic arguments are put aside, the motivation isn't really there. And honestly, it's a bit weird to me to realise how relatively okay I am with glob imports in Rust, considering how I often despise them in other languages like JavaScript. The main reason for this is that basically all of the tools in the Rust ecosystem directly interface with compiler internals one way or another, even if by reimplementing parts of the compiler in the case of In the JS ecosystem, if you see a glob import, all hope is essentially lost. You can try and strip away all of the unreasonable ways of interfacing with names like eval but ultimately, unless you want to reimplement the module system yourself and do a lot of work, a person seeing a glob import knows as much as a machine reading it does. This isn't the case for Rust, and something like So really, this is an aesthetic argument. And honestly… I don't think that importing everything by glob, or by name, is really that big a deal, especially with adequate tooling. Even renaming things. Ultimately, I'm not super against this feature in principle. But I'm also not really sure if it's worth it. Rust's type inference is robust and I don't think it would run into technical issues, just… I don't really know if it's worth the effort. |
|
@clarfonthey glob imports easily have name collision when using multiple globs in the same module. And it is really common with names like |
I can understand your point, but, when using large libraries in conjunction, like @SOF3 said, it can be easy to run into name collisions. I use actix and seaorm and they often have simular type names. |
|
Right, I should probably clarify my position-- I think that not liking globs is valid, but I also think that using globs is more viable in Rust than in other languages. Meaning, it's both easier to use globs successfully, and also easier to just import everything you need successfully. Rebinding is a bit harder, but still doable. Since seeing how useful Even if you're specifically scoping various types to modules since they conflict, that's still just the first letter of the module, autocomplete, two colons, the first letter of the type, autocomplete. Which may be more to type than My main opinion here is that Like, I'm not convinced that this can't be better solved by improving APIs. Like, for example, you mentioned that types commonly in preludes for different crates used together often share names. I think that this is bad API design, personally, but maybe I'm just not getting it. |
|
I do think inferred types are useful when matching for brevity's sake: #[derive(Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
pub struct Reg(pub Option<NonZeroU8>);
#[derive(Debug)]
pub struct Regs {
pub pc: u32,
pub regs: [u32; 31],
}
impl Regs {
pub fn reg(&self, reg: Reg) -> u32 {
reg.0.map_or(0, |reg| self.regs[reg.get() - 1])
}
pub fn set_reg(&mut self, reg: Reg, value: u32) {
if let Some(reg) = reg {
self.regs[reg.get() - 1] = value;
}
}
}
#[derive(Debug)]
pub struct Memory {
bytes: Box<[u8]>,
}
impl Memory {
pub fn read_bytes<const N: usize>(&self, mut addr: u32) -> [u8; N] {
let mut retval = [0u8; N];
for v in &mut retval {
*v = self.bytes[addr.try_into().unwrap()];
addr = addr.wrapping_add(1);
}
retval
}
pub fn write_bytes<const N: usize>(&mut self, mut addr: u32, bytes: [u8; N]) {
for v in bytes {
self.bytes[addr.try_into().unwrap()] = v;
addr = addr.wrapping_add(1);
}
}
}
pub fn run_one_insn(regs: &mut Regs, mem: &mut Memory) {
let insn = Insn::decode(u32::from_le_bytes(mem.read_bytes(regs.pc))).unwrap();
match insn {
_::RType(_ { rd, rs1, rs2, rest: _::Add }) => {
regs.set_reg(rd, regs.reg(rs1).wrapping_add(regs.reg(rs2)));
}
_::RType(_ { rd, rs1, rs2, rest: _::Sub }) => {
regs.set_reg(rd, regs.reg(rs1).wrapping_sub(regs.reg(rs2)));
}
_::RType(_ { rd, rs1, rs2, rest: _::Sll }) => {
regs.set_reg(rd, regs.reg(rs1).wrapping_shl(regs.reg(rs2)));
}
_::RType(_ { rd, rs1, rs2, rest: _::Slt }) => {
regs.set_reg(rd, ((regs.reg(rs1) as i32) < regs.reg(rs2) as i32) as u32);
}
_::RType(_ { rd, rs1, rs2, rest: _::Sltu }) => {
regs.set_reg(rd, (regs.reg(rs1) < regs.reg(rs2)) as u32);
}
// ...
_::IType(_ { rd, rs1, imm, rest: _::Jalr }) => {
let pc = regs.reg(rs1).wrapping_add(imm as u32) & !1;
regs.set_reg(rd, regs.pc.wrapping_add(4));
regs.pc = pc;
return;
}
_::IType(_ { rd, rs1, imm, rest: _::Lb }) => {
let [v] = mem.read_bytes(regs.reg(rs1).wrapping_add(imm as u32));
regs.set_reg(rd, v as i8 as u32);
}
_::IType(_ { rd, rs1, imm, rest: _::Lh }) => {
let v = mem.read_bytes(regs.reg(rs1).wrapping_add(imm as u32));
regs.set_reg(rd, i16::from_le_bytes(v) as u32);
}
_::IType(_ { rd, rs1, imm, rest: _::Lw }) => {
let v = mem.read_bytes(regs.reg(rs1).wrapping_add(imm as u32));
regs.set_reg(rd, u32::from_le_bytes(v));
}
// ...
}
regs.pc = regs.pc.wrapping_add(4);
}
pub enum Insn {
RType(RTypeInsn),
IType(ITypeInsn),
SType(STypeInsn),
BType(BTypeInsn),
UType(UTypeInsn),
JType(JTypeInsn),
}
impl Insn {
pub fn decode(v: u32) -> Option<Self> {
// ...
}
}
pub struct RTypeInsn {
pub rd: Reg,
pub rs1: Reg,
pub rs2: Reg,
pub rest: RTypeInsnRest,
}
pub enum RTypeInsnRest {
Add,
Sub,
Sll,
Slt,
Sltu,
Xor,
Srl,
Sra,
Or,
And,
}
pub struct ITypeInsn {
pub rd: Reg,
pub rs1: Reg,
pub imm: i16,
pub rest: ITypeInsnRest,
}
pub enum ITypeInsnRest {
Jalr,
Lb,
Lh,
Lw,
Lbu,
Lhu,
Addi,
Slti,
Sltiu,
Xori,
Ori,
Andi,
Slli,
Srli,
Srai,
Fence,
FenceTso,
Pause,
Ecall,
Ebreak,
}
// rest of enums ... |
|
I do like type inference for struct literals and enum variants. However, type inference for associated functions doesn't make sense to me. Given this example: fn expect_foo(_: Foo) {}
foo(_::bar());
All in all, it feels like this would add a lot of complexity and make the language less consistent and harder to learn. Footnotes
|
|
Regarding structs and enums: The RFC didn't explicitly mention this, but I think that tuple structs, tuple enum variants, and unit structs should also be inferrable: enum MyEnum {
NormalVariant { a: i32 },
TupleVariant(i32),
UnitVariant,
}
struct NormalStruct { a: i32 }
struct TupleStruct(i32);
struct UnitStruct;
fn expect_enum(_: MyEnum) {}
fn expect_normal_struct(_: NormalStruct) {}
fn expect_tuple_struct(_: TupleStruct) {}
fn expect_unit_struct(_: UnitStruct) {}
expect_enum(_::NormalVariant { a: 42 });
expect_enum(_::TupleVariant(42));
expect_enum(_::UnitVariant);
expect_normal_struct(_ { a: 42 });
expect_tuple_struct(_(42));
expect_unit_struct(_); |
|
I use a trait which contains a function that takes an argument of a type which is only accessed as an associated type, and being able to replace all of that with a |
Just gonna break down my thought here when I read this:
I'm not sure what's gained by doing this instead of adding a single |
As mentioned in the quoted comment "With the proposed and accepted default fields syntax" that
Since |
|
I would find the struct literal case immediately useful in macro implementations. I have macros that generate bidirectional mappings between two types -- think Ideally, the macro could generate expressions like |
I think this feature will be used a lot if added, and worth the dedication to it. I think it would be simpler to remember rules of a new single sigil when the intent is for this type inference. All the context you really need for the use is an indicator of the intent for this inference. Same reason I would imagine a single For example, having some places that have For some formatting issues I have: fn foo(_ {bar, baz}: BarBaz) {}Or in the nested case above: T { foo: _ { bar: _ { baz: ... } } }These look less succinct compared to: fn foo(.{bar, baz}: BarBaz) {}
T { foo: .{ bar: .{ baz: ... } } }We already have Like combining: fn foo(_ {bar, _ { foo: _ { bar: _::Variant { baz: ... } } }}: Thing) {}
fn foo(.{bar, .{ foo: .{ bar: .Variant { baz: ... } } }}: Thing) {}To my eyes, its clear which reads better. I think it conveys the actual intent wanted better as well. Words are escaping me on how I should best explain my overall view here on Its like while
If this doesn't make sense then feel free to ignore. Its not meant to be taken exactly, so I only mentioned the parts where it matches what I am trying to convey. I plan on thinking more about the sigil and trying to give a more technical breakdown when I get a better idea of how to convey it. Part of this will include how syntax highlighting would be different, like how for an enum, Things like text search are interesting to think about, and actually having the different types of And thinking on alternatives, this actually brings up an interesting question: If anyone has context why it was decided to move away from |
Yeah, on thinking more, I would agree, the solution should be found for all of the related types in this single RFC for a more cohesive feel. And in thinking on enums, I had thought of this example, where the let connection = Connection::open(_::Https {
host: "localhost",
port: 5672,
username: "user",
password: "bitnami",
..
})
.await
.unwrap();Where as this seems much cleaner: let connection = Connection::open(.Https {
host: "localhost",
port: 5672,
username: "user",
password: "bitnami",
..
})
.await
.unwrap();I definitely don't want to have different syntax for the use sites with enums, like some places use |
I may be leaning too much into the metaphor - but it's not an electron. One important property of electrons is that they are all the same. All electrons have the exact same physical properties (same size, same mass, same charge, same spin). But here these expressions are resolved to different types, so it's more akin to atoms which can have different types1. The way I imagine the electron metaphor is that something like This is not how it will work, because Rust has no such magic ad-hoc struct type. With atoms, you can't just say "this is here is an atom" - you need to tell the compiler which type of atom. This is what you do in the current syntax, where you have to specify the type - Footnotes
|
|
Is it possible to separate this into 2 parts? One for the enum and one for the other type inference, I think enum is still fine, but |
You are calling a function named |
I think it depends. Compared to And const foo: Foo = Foo { field: 1 }is redundent, but Or consider something like: MyEnum::Foo(Foo{ field: 1 }) |
This is already mostly true now. For example: let connection = Connection::open(ConnectionArguments::new(
"localhost",
5672,
"user",
"bitnami",
))
.await
.unwrap();This might be an easy case if you already knew what these things mean in this specific context, but this kind of bag of values, with no variables naming these values, is very common in a lot of patterns. Having fields named what the arguments are is a plus for readability IMO. Its like a function that takes three bools. At least if the fields were named it would go a lot farther to add context for what you actually mean. open("/path/to/thing", false, false, true);Im not saying this is good API design, but having: struct OpenOptions {
path: &Path,
is_compressed: bool,
is_encrypted: bool,
is_ascii_only: bool
}
fn open(path: &Path, options: OpenOptions) { ... }
open(.{
path: &Path::new("/path/to/thing"),
is_compressed: false,
is_encrypted: false,
is_ascii_only: true
});seems a lot better for local readability. This example is probably better served handled differently, like with enums with good names for the variants, but it gets idea across. And even here, The biggest areas of improvements, to me, would be something along the lines of the As an example of the API: CreateWindowExW<P1, P2>(
dwexstyle: WINDOW_EX_STYLE,
lpclassname: P1,
lpwindowname: P2,
dwstyle: WINDOW_STYLE,
x: i32,
y: i32,
nwidth: i32,
nheight: i32,
hwndparent: Option<HWND>,
hmenu: Option<HMENU>,
hinstance: Option<HINSTANCE>,
lpparam: Option<*const c_void>,
)You could end up seeing (Not saying this is actual correct usage!): CreateWindowExW(
WINDOW_EX_STYLE(0),
..,
..,
WINDOW_STYLE(0),
0,
0,
800,
800,
None,
None,
None,
None
)Having: CreateWindowExW(.{
dwexstyle: WINDOW_EX_STYLE(0),
lpclassname: ..,
lpwindowname: ..,
dwstyle: WINDOW_STYLE(0),
x: 0,
y: 0,
nwidth: 800,
nheight: 800,
hwndparent: None,
hmenu: None,
hinstance: None,
lpparam: None
})I think this is a clear win in terms of local readability. This isn't a silver bullet for API design, a lot of thought still needs to go into it, but its a missing ergonomic tool in the rust tool bag right now. Its not that there wont be places this is ill suited for, and if abused can be a mess, but that's true of any rust feature. |
|
The use windows::{
core::*, Data::Xml::Dom::*, Win32::Foundation::*, Win32::System::Threading::*,
Win32::UI::WindowsAndMessaging::*,
};
fn main() -> Result<()> {
let doc = XmlDocument::new()?;
doc.LoadXml(h!("<html>hello world</html>"))?;
let root = doc.DocumentElement()?;
assert!(root.NodeName()? == "html");
assert!(root.InnerText()? == "hello world");
unsafe {
let event = CreateEventW(None, true, false, None)?;
SetEvent(event)?;
WaitForSingleObject(event, 0);
CloseHandle(event)?;
MessageBoxA(None, s!("Ansi"), s!("Caption"), MB_OK);
MessageBoxW(None, w!("Wide"), w!("Caption"), MB_OK);
}
Ok(())
}To hone in on: let event = CreateEventW(None, true, false, None)?;We could have: let event = CreateEventW(.{
lpeventattributes: None,
bmanualreset: true,
binitialstate: false,
lpname: None
)?;You could imagine what |
|
An anecdotal example of someone wanting to improve ergonomics (though I would disagree that the chosen method is better, or even good): https://www.reddit.com/r/rust/comments/1qd9sra/i_wish_rust_had_keyword_arguments/ |
This is so cool! Thanks for sharing. I will check out the crate. Additionally, if anyone wants to be part of the design meeting for making this an experiment rust-lang/lang-team#361 |
|
The That is, I think that the feature described in this RFC would probably cause the cc @rust-lang/rust-for-linux (owner of the Additionally, since this feature can be emulated with the TAIT feature with the next-solver, this means that the TAIT feature with the next-solver will cause the I have also described this issue at rust-lang/rust#153535 |
Can you provide a code sample of what this would allow? Thanks. |
|
@JoshBashed Based on rust-lang/rust#153535, something like this Detailsuse std::pin::Pin;
use pinned_init::{init, stack_pin_init};
struct Thing<T> {
field: T,
}
fn conjure<T>() -> T {
panic!()
}
#[expect(unreachable_code)]
fn make_uninit<T: Unpin>(dummy: T) -> T {
let initializer = init!(Thing {
field <- {
// This will be inferred to the unnamable __InitOk type
return Ok(.{});
conjure::<T>()
}
});
stack_pin_init!(let pinned_value = initializer);
let mut pinned_value: Pin<&mut Thing<T>> = pinned_value;
let mut_value: &mut T = &mut pinned_value.field;
std::mem::replace(mut_value, dummy)
}
fn main() {
println!("{}", make_uninit::<Box<i32>>(Box::new(1)));
} |
|
We could just exclude unit structs from the scope of this proposal, just as union's aren't being included. This kind of interference doesn't really make much sense with them anyways. This would then be scoped to only be for objects where there is a "named field" to infer, i.e named enum variants and struct fields (even if the field name is just What real use case would there be for |
That is possible, but adding such inconsistency doesn't make a lot of sense IMO as a "fix", what if people rely on Though such a problem is worth noting, there are a number of crates in the ecosystem that use token structs for safety, it would great to know which ones are problematic. |
is something like this not feasible over just mod __init_ok_mod {
use std::marker::PhantomData;
pub(super) struct __InitOk(pub(self) PhantomData<()>);
pub(super) fn create() -> __InitOk { __InitOk(PhantomData) }
} |
|
It seems to me like maybe the ideal solution to this would be if constructing __InitOk required the use of unsafe, either because it contains a ZST field that requires unsafe to construct, or the struct is marked as being unsafe. Or maybe have a way to restrict creation of a struct to a lexical scope? |
|
That solution requires an additional There was a specific historical reason for why we did things this way, see @nbdd0121's comment for more information. But with the design we ended up in the end, this isn't actually needed. I explain in rust-lang/rust#153535 (comment) what I'm going to do. Essentially we move the |
|
Another possibility is to not allow path inference to access a type that is shadowed by another type. Although that feels kind of weird to me. That said, relying on shadowing to prevent creation of a struct does seem kind of hacky to me, and assumes that rust will never add a feature that would allow referencing the shadowed type (which, as it turns out is something we would like to do). |
|
I really wish this whole encapsulation trick could have just been made explicit. Some way to mark the type as unbuildable outside a certain scope (that's not a module scope) without having to rely on language limitations. |
We use a unit struct `__InitOk` in the closure generated by the
initializer macros as the return value. We shadow it by creating a
struct with the same name again inside of the closure, preventing early
returns of `Ok` in the initializer (before all fields have been
initialized).
In the face of Type Alias Impl Trait (TAIT) and the next trait solver,
this solution no longer works [1]. The shadowed struct can be named
through type inference. In addition, there is an RFC proposing to add
the feature of path inference to Rust, which would similarly allow [2]
Thus remove the shadowed token and replace it with an `unsafe` to create
token.
The reason we initially used the shadowing solution was because an
alternative solution used a builder pattern. Gary writes [3]:
In the early builder-pattern based InitOk, having a single InitOk
type for token is unsound because one can launder an InitOk token
used for one place to another initializer. I used a branded lifetime
solution, and then you figured out that using a shadowed type would
work better because nobody could construct it at all.
The laundering issue does not apply to the approach we ended up with
today.
With this change, the example by Tim Chirananthavat in [1] no longer
compiles and results in this error:
error: cannot construct `pin_init::__internal::InitOk` with struct literal syntax due to private fields
--> src/main.rs:26:17
|
26 | InferredType {}
| ^^^^^^^^^^^^
|
= note: private field `0` that was not provided
help: you might have meant to use the `new` associated function
|
26 - InferredType {}
26 + InferredType::new()
|
Applying the suggestion of using the `::new()` function, results in
another expected error:
error[E0133]: call to unsafe function `pin_init::__internal::InitOk::new` is unsafe and requires unsafe block
--> src/main.rs:26:17
|
26 | InferredType::new()
| ^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
Reported-by: Tim Chirananthavat <theemathas@gmail.com>
Link: rust-lang/rust#153535 [1]
Link: rust-lang/rfcs#3444 (comment) [2]
Link: rust-lang/rust#153535 (comment) [3]
Signed-off-by: Benno Lossin <lossin@kernel.org>
We use a unit struct `__InitOk` in the closure generated by the
initializer macros as the return value. We shadow it by creating a
struct with the same name again inside of the closure, preventing early
returns of `Ok` in the initializer (before all fields have been
initialized).
In the face of Type Alias Impl Trait (TAIT) and the next trait solver,
this solution no longer works [1]. The shadowed struct can be named
through type inference. In addition, there is an RFC proposing to add
the feature of path inference to Rust, which would similarly allow [2]
Thus remove the shadowed token and replace it with an `unsafe` to create
token.
The reason we initially used the shadowing solution was because an
alternative solution used a builder pattern. Gary writes [3]:
In the early builder-pattern based InitOk, having a single InitOk
type for token is unsound because one can launder an InitOk token
used for one place to another initializer. I used a branded lifetime
solution, and then you figured out that using a shadowed type would
work better because nobody could construct it at all.
The laundering issue does not apply to the approach we ended up with
today.
With this change, the example by Tim Chirananthavat in [1] no longer
compiles and results in this error:
error: cannot construct `pin_init::__internal::InitOk` with struct literal syntax due to private fields
--> src/main.rs:26:17
|
26 | InferredType {}
| ^^^^^^^^^^^^
|
= note: private field `0` that was not provided
help: you might have meant to use the `new` associated function
|
26 - InferredType {}
26 + InferredType::new()
|
Applying the suggestion of using the `::new()` function, results in
another expected error:
error[E0133]: call to unsafe function `pin_init::__internal::InitOk::new` is unsafe and requires unsafe block
--> src/main.rs:26:17
|
26 | InferredType::new()
| ^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
Reported-by: Tim Chirananthavat <theemathas@gmail.com>
Link: rust-lang/rust#153535 [1]
Link: rust-lang/rfcs#3444 (comment) [2]
Link: rust-lang/rust#153535 (comment) [3]
Signed-off-by: Benno Lossin <lossin@kernel.org>
We use a unit struct `__InitOk` in the closure generated by the
initializer macros as the return value. We shadow it by creating a
struct with the same name again inside of the closure, preventing early
returns of `Ok` in the initializer (before all fields have been
initialized).
In the face of Type Alias Impl Trait (TAIT) and the next trait solver,
this solution no longer works [1]. The shadowed struct can be named
through type inference. In addition, there is an RFC proposing to add
the feature of path inference to Rust, which would similarly allow [2]
Thus remove the shadowed token and replace it with an `unsafe` to create
token.
The reason we initially used the shadowing solution was because an
alternative solution used a builder pattern. Gary writes [3]:
In the early builder-pattern based InitOk, having a single InitOk
type for token is unsound because one can launder an InitOk token
used for one place to another initializer. I used a branded lifetime
solution, and then you figured out that using a shadowed type would
work better because nobody could construct it at all.
The laundering issue does not apply to the approach we ended up with
today.
With this change, the example by Tim Chirananthavat in [1] no longer
compiles and results in this error:
error: cannot construct `pin_init::__internal::InitOk` with struct literal syntax due to private fields
--> src/main.rs:26:17
|
26 | InferredType {}
| ^^^^^^^^^^^^
|
= note: private field `0` that was not provided
help: you might have meant to use the `new` associated function
|
26 - InferredType {}
26 + InferredType::new()
|
Applying the suggestion of using the `::new()` function, results in
another expected error:
error[E0133]: call to unsafe function `pin_init::__internal::InitOk::new` is unsafe and requires unsafe block
--> src/main.rs:26:17
|
26 | InferredType::new()
| ^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
Reported-by: Tim Chirananthavat <theemathas@gmail.com>
Link: rust-lang/rust#153535 [1]
Link: rust-lang/rfcs#3444 (comment) [2]
Link: rust-lang/rust#153535 (comment) [3]
Signed-off-by: Benno Lossin <lossin@kernel.org>



This RFC proposes a leading-dot syntax for path inference in type construction and pattern matching. When the expected type is known from context, developers can write
.Variant,.Variant { … },.Variant(…),.{ … }, or.(…)instead of typing the full type name.Rendered