{"record":{"id":"99ca5b8360466012","repo":"databendlabs/databend","slug":"typed-builder-must-produce-exactly-one-function","errorCode":null,"errorMessage":"typed builder must produce exactly one function","messagePattern":"typed builder must produce exactly one function","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/query/expression/src/function/function_builder.rs","lineNumber":288,"sourceCode":"        &self.name\n    }\n\n    fn collect(&mut self, function: Function) {\n        self.functions.push(function);\n    }\n}\n\nimpl InlineFunctionBuilder {\n    pub fn new(name: impl Into<String>) -> Self {\n        Self {\n            name: name.into(),\n            function: None,\n        }\n    }\n\n    pub fn finish(self) -> Function {\n        self.function\n            .expect(\"typed builder must produce exactly one function\")\n    }\n}\n\nimpl ScalarFunctionCollect for InlineFunctionBuilder {\n    const FOR_FACTORY: bool = true;\n\n    fn name(&self) -> &str {\n        &self.name\n    }\n\n    fn collect(&mut self, function: Function) {\n        assert!(self.function.is_none(), \"function already built\");\n        self.function = Some(function);\n    }\n}\n\npub struct ScalarFunctionArityBuilder<B> {\n    builder: B,","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/databendlabs/databend/blob/288d84d76e20a2f8f7173bda9691eb6ece301aa9/src/query/expression/src/function/function_builder.rs#L270-L306","documentation":"`InlineFunctionBuilder::finish` panics when the internal `function` field is still `None`, meaning no function was ever stored in the typed builder. The builder API is designed so exactly one function is produced before `finish` is called; `expect` enforces this invariant loudly. This is a builder lifecycle misuse rather than a runtime data problem.","triggerScenarios":"Calling `.finish()` on an `InlineFunctionBuilder` without having called the builder's function-producing step (the method that sets `self.function = Some(...)`), or calling `finish()` twice after the builder was consumed/reset.","commonSituations":"Writing a new scalar function registration and forgetting the intermediate build step; copy-pasting a builder chain and dropping the line that constructs the function; conditional code paths that skip the build call.","solutions":["Ensure the builder's function-producing method is called exactly once before `finish()`.","Chain the calls fluently (`...build(...).finish()`) so the compiler/ordering guarantees the function is set.","If building conditionally, make sure every branch sets the function before finishing."],"exampleFix":"// before\nlet builder = InlineFunctionBuilder::new(\"my_func\");\nlet f = builder.finish(); // panics: function never set\n// after\nlet f = InlineFunctionBuilder::new(\"my_func\").build(params, body).finish();","handlingStrategy":"type-guard","validationCode":"// before finish():\ndebug_assert!(builder.function.is_some(), \"finish() called before function was built\");","typeGuard":"fn ready_to_finish(b: &InlineFunctionBuilder) -> bool { b.function.is_some() }","tryCatchPattern":"// prefer guarding; panics from expect are unrecoverable in Rust:\nif !ready_to_finish(&builder) { return Err(ErrorCode::Internal(\"function not built\")); }","preventionTips":["Use fluent chaining so build-then-finish ordering is enforced by construction.","Never store builders across conditional branches that may skip the build step.","Code-review new function registrations for complete builder chains."],"tags":["rust","builder-pattern","function-registry"],"backgroundTag":"invalid-state-transition","analyzedSha":"288d84d76e20a2f8f7173bda9691eb6ece301aa9","analyzedAt":"2026-09-11T11:29:36.208Z","contentChangedAt":"2026-09-11T11:29:36.208Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}